package srsim.io;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.Socket;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import srsim.domain.Room;
import srsim.simulator.ISimulationController;
import srsim.simulator.SimulationContext;
import srsim.simulator.SimulationContextException;
/**
* Client connection class to handle communication with clients
*
* @author phil
*
*/
public class ClientConnection extends Thread {
private static final String FAILURE_MSG = "ERR";
private static final String SUCCESS_MSG = "OK";
private static final String ROOM_LIST_MSG = "LIST_ROOMS";
private static final String UPDATE_REQUEST_MSG = "UPDATE_REQUEST";
private static final int RECIEVE_BUFFER_SIZE = 1024;
private Socket clientSocket;
private PreferencesServer server;
private Map<String, List<String>> preferences;
private String roomId;
public ClientConnection(final Socket clientSocket,
final PreferencesServer server) {
this.clientSocket = clientSocket;
this.server = server;
preferences = new HashMap<String, List<String>>();
roomId = null;
setName("SRSIM Client " + clientSocket.getInetAddress());
setDaemon(true);
start();
}
@Override
public void run() {
try {
handleConnection();
} catch (IOException e) {
System.out.println("Client "
+ clientSocket.getInetAddress().getHostAddress()
+ " lost connection");
} catch (SimulationContextException e) {
e.printStackTrace();
} finally {
try {
unsetPreferences();
} catch (SimulationContextException e) {
e.printStackTrace();
}
server.setClientLocation(clientSocket.getInetAddress()
.getHostAddress(), null);
}
}
/**
* Handles the connection, i.e. Reads from the input stream and replies with
* "OK" whenever a message has been successfully processed.
*
* @throws IOException
* @throws SimulationContextException
*/
private void handleConnection() throws IOException,
SimulationContextException {
InputStreamReader reader;
reader = new InputStreamReader(clientSocket.getInputStream());
char[] buffer = new char[RECIEVE_BUFFER_SIZE];
int len = reader.read(buffer);
while (len >= 0) {
String msg = new String(buffer, 0, len);
if (msg.trim().equalsIgnoreCase(UPDATE_REQUEST_MSG)) {
JsonObject contextUpdate = buildContextUpdate();
String string = contextUpdate.toString();
clientSocket.getOutputStream().write(string.getBytes());
} else if (msg.trim().equalsIgnoreCase(ROOM_LIST_MSG)) {
JsonArrayBuilder roomArrayBuilder = Json.createArrayBuilder();
for (Room room : server.getController().getRooms()) {
roomArrayBuilder.add(room.getName());
}
clientSocket.getOutputStream().write(
roomArrayBuilder.build().toString().getBytes());
} else if (msg.startsWith("{")) {
try {
parseMessage(msg);
clientSocket.getOutputStream()
.write(SUCCESS_MSG.getBytes());
} catch (SimulationContextException e) {
clientSocket.getOutputStream()
.write(FAILURE_MSG.getBytes());
}
}
buffer = new char[RECIEVE_BUFFER_SIZE];
len = reader.read(buffer);
}
}
/**
* Builds a JSON representation of a simulation record
*
* @return
* @throws SimulationContextException
*/
private JsonObject buildContextUpdate() throws SimulationContextException {
ISimulationController controller = server.getController();
List<Room> rooms = controller.getRooms();
JsonObjectBuilder contextUpdateBuilder = Json.createObjectBuilder();
if (rooms != null) {
for (Room room : rooms) {
String name = room.getName();
if (name.equalsIgnoreCase(roomId)) {
SimulationContext context = room.getLocalContext();
int[] lightColor = context.getLightColor();
contextUpdateBuilder
.add(SimulationContext.BRIGHTNESS,
context.getBrightness())
.add(SimulationContext.TEMPERATURE,
context.getTemperature())
.add(SimulationContext.LIGHTCOLOR,
String.format("%d %d %d", lightColor[0],
lightColor[1], lightColor[2]))
.add(SimulationContext.ENERGY_CONSUMPTION,
context.getEnergyConsumption())
.add(SimulationContext.MUSIC_GENRE,
context.getMusicGenre())
.add(SimulationContext.MUSIC_VOLUME,
context.getMusicVolume());
}
}
}
JsonObject contextUpdate = contextUpdateBuilder.build();
return contextUpdate;
}
/**
* Unsets all prefrences set by the client
*
* @throws SimulationContextException
*/
private void unsetPreferences() throws SimulationContextException {
for (String key : preferences.keySet()) {
for (String value : preferences.get(key)) {
server.unsetPreference(roomId, key, value);
}
}
}
/**
* Processes a JSON message
*
* @param msg
* @throws SimulationContextException
*/
private void parseMessage(final String msg)
throws SimulationContextException {
JsonObject jsonObject = Json.createReader(new StringReader(msg))
.readObject();
for (String key : jsonObject.keySet()) {
String value = jsonObject.getString(key).trim();
if (key.trim().equalsIgnoreCase("roomId")) {
roomId = value;
server.setClientLocation(clientSocket.getInetAddress()
.getHostAddress(), roomId);
} else {
setPreference(key.trim(), value);
}
}
}
/**
* Sets a preference
*
* @param key
* @param value
* @throws SimulationContextException
*/
private void setPreference(final String key, final String value)
throws SimulationContextException {
server.setPreference(roomId, key, value);
if (!preferences.keySet().contains(key)) {
preferences.put(key, new LinkedList<String>());
}
preferences.get(key).add(value);
}
public void close() throws IOException {
clientSocket.close();
}
}