// Copyright 2015 ThoughtWorks, Inc.
// This file is part of Gauge-Java.
// This program is free software.
//
// It is dual-licensed under:
// 1) the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version;
// or
// 2) the Eclipse Public License v1.0.
//
// You can redistribute it and/or modify it under the terms of either license.
// We would then provide copied of each license in a separate .txt file with the name of the license as the title of the file.
package com.thoughtworks.gauge.connection;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.thoughtworks.gauge.ClassInstanceManager;
import com.thoughtworks.gauge.datastore.DataStoreInitializer;
import com.thoughtworks.gauge.processor.ExecuteStepProcessor;
import com.thoughtworks.gauge.processor.IMessageProcessor;
import com.thoughtworks.gauge.processor.KillProcessProcessor;
import com.thoughtworks.gauge.processor.RefactorRequestProcessor;
import com.thoughtworks.gauge.processor.ScenarioExecutionEndingProcessor;
import com.thoughtworks.gauge.processor.ScenarioExecutionStartingProcessor;
import com.thoughtworks.gauge.processor.SpecExecutionEndingProcessor;
import com.thoughtworks.gauge.processor.SpecExecutionStartingProcessor;
import com.thoughtworks.gauge.processor.StepExecutionEndingProcessor;
import com.thoughtworks.gauge.processor.StepExecutionStartingProcessor;
import com.thoughtworks.gauge.processor.StepNameRequestProcessor;
import com.thoughtworks.gauge.processor.StepNamesRequestProcessor;
import com.thoughtworks.gauge.processor.SuiteExecutionEndingProcessor;
import com.thoughtworks.gauge.processor.SuiteExecutionStartingProcessor;
import com.thoughtworks.gauge.processor.ValidateStepProcessor;
import gauge.messages.Messages;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
/**
* Receives messages from gauge core and processes them using the relevant MessageProcessor and returns a
* valid response.
*/
public class MessageDispatcher {
private final HashMap<Messages.Message.MessageType, IMessageProcessor> messageProcessors;
public MessageDispatcher() {
final ClassInstanceManager instanceManager = new ClassInstanceManager();
messageProcessors = new HashMap<Messages.Message.MessageType, IMessageProcessor>() {{
put(Messages.Message.MessageType.ExecutionStarting, new SuiteExecutionStartingProcessor(instanceManager));
put(Messages.Message.MessageType.ExecutionEnding, new SuiteExecutionEndingProcessor(instanceManager));
put(Messages.Message.MessageType.SpecExecutionStarting, new SpecExecutionStartingProcessor(instanceManager));
put(Messages.Message.MessageType.SpecExecutionEnding, new SpecExecutionEndingProcessor(instanceManager));
put(Messages.Message.MessageType.ScenarioExecutionStarting, new ScenarioExecutionStartingProcessor(instanceManager));
put(Messages.Message.MessageType.ScenarioExecutionEnding, new ScenarioExecutionEndingProcessor(instanceManager));
put(Messages.Message.MessageType.StepExecutionStarting, new StepExecutionStartingProcessor(instanceManager));
put(Messages.Message.MessageType.StepExecutionEnding, new StepExecutionEndingProcessor(instanceManager));
put(Messages.Message.MessageType.ExecuteStep, new ExecuteStepProcessor(instanceManager));
put(Messages.Message.MessageType.StepValidateRequest, new ValidateStepProcessor(instanceManager));
put(Messages.Message.MessageType.StepNamesRequest, new StepNamesRequestProcessor(instanceManager));
put(Messages.Message.MessageType.SuiteDataStoreInit, new DataStoreInitializer(instanceManager));
put(Messages.Message.MessageType.SpecDataStoreInit, new DataStoreInitializer(instanceManager));
put(Messages.Message.MessageType.ScenarioDataStoreInit, new DataStoreInitializer(instanceManager));
put(Messages.Message.MessageType.KillProcessRequest, new KillProcessProcessor(instanceManager));
put(Messages.Message.MessageType.StepNameRequest, new StepNameRequestProcessor(instanceManager));
put(Messages.Message.MessageType.RefactorRequest, new RefactorRequestProcessor(instanceManager));
}};
}
public void dispatchMessages(GaugeConnector connector) throws IOException {
Socket gaugeSocket = connector.getGaugeSocket();
InputStream inputStream = gaugeSocket.getInputStream();
while (isConnected(gaugeSocket)) {
try {
MessageLength messageLength = getMessageLength(inputStream);
byte[] bytes = toBytes(messageLength);
Messages.Message message = Messages.Message.parseFrom(bytes);
if (!messageProcessors.containsKey(message.getMessageType())) {
System.err.println("Invalid message type received " + message.getMessageType());
} else {
IMessageProcessor messageProcessor = messageProcessors.get(message.getMessageType());
Messages.Message response = messageProcessor.process(message);
writeMessage(gaugeSocket, response);
if (message.getMessageType() == Messages.Message.MessageType.KillProcessRequest) {
gaugeSocket.close();
return;
}
}
} catch (InvalidProtocolBufferException e) {
return;
} catch (Throwable throwable) {
throwable.printStackTrace();
System.err.println(throwable.toString());
return;
}
}
}
private MessageLength getMessageLength(InputStream is) throws IOException {
CodedInputStream codedInputStream = CodedInputStream.newInstance(is);
long size = codedInputStream.readRawVarint64();
return new MessageLength(size, codedInputStream);
}
private byte[] toBytes(MessageLength messageLength) throws IOException {
long messageSize = messageLength.getLength();
CodedInputStream stream = messageLength.getRemainingStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
for (int i = 0; i < messageSize; i++) {
outputStream.write(stream.readRawByte());
}
return outputStream.toByteArray();
}
private void writeMessage(Socket socket, Messages.Message message) throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
CodedOutputStream cos = CodedOutputStream.newInstance(stream);
byte[] bytes = message.toByteArray();
cos.writeRawVarint64(bytes.length);
cos.flush();
stream.write(bytes);
socket.getOutputStream().write(stream.toByteArray());
socket.getOutputStream().flush();
}
private boolean isConnected(Socket socket) {
return !socket.isClosed() && socket.isConnected();
}
}