/* * WorkerClient.java * * Copyright (C) 2015 Pixelgaffer * * This work is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation; either version 2 of the License, or any later * version. * * This work is distributed in the hope that it will be useful, but without * any warranty; without even the implied warranty of merchantability or * fitness for a particular purpose. See version 2 and version 3 of the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.pixelgaffer.turnierserver.sandboxmanager; import static java.nio.charset.StandardCharsets.UTF_8; import static org.pixelgaffer.turnierserver.PropertyUtils.RECON_IVAL; import static org.pixelgaffer.turnierserver.PropertyUtils.getIntRequired; import static org.pixelgaffer.turnierserver.PropertyUtils.getStringRequired; import static org.pixelgaffer.turnierserver.networking.NetworkService.getService; import static org.pixelgaffer.turnierserver.networking.messages.SandboxCommand.CPU_TIME; import static org.pixelgaffer.turnierserver.networking.messages.SandboxCommand.KILL_AI; import static org.pixelgaffer.turnierserver.networking.messages.SandboxCommand.RUN_AI; import static org.pixelgaffer.turnierserver.networking.messages.SandboxCommand.TERM_AI; import static org.pixelgaffer.turnierserver.sandboxmanager.SandboxMain.commands; import java.io.IOException; import java.util.UUID; import org.json.JSONArray; import org.json.JSONObject; import org.pixelgaffer.turnierserver.Airbrake; import org.pixelgaffer.turnierserver.Parsers; import org.pixelgaffer.turnierserver.PropertyUtils; import org.pixelgaffer.turnierserver.networking.NetworkService; import org.pixelgaffer.turnierserver.networking.messages.SandboxCommand; import org.pixelgaffer.turnierserver.networking.util.DataBuffer; import lombok.Getter; import naga.NIOSocket; import naga.SocketObserver; public class WorkerClient implements SocketObserver { NIOSocket client; @Getter private String host; @Getter private int port; @Getter private boolean connected; private final DataBuffer buf = new DataBuffer(); @Getter private final JobControl jobControl = new JobControl(); public WorkerClient () throws IOException { host = getStringRequired("worker.host"); port = getIntRequired("worker.port"); client = getService().openSocket(host, port); client.listen(this); } public void sendMessage (UUID uuid, char event) { JSONObject json = new JSONObject(); json.put("uuid", uuid); json.put("event", Character.toString(event)); client.write((json + "\n").getBytes(UTF_8)); } public void sendMessage (UUID uuid, char event, long cpuTime) { JSONObject json = new JSONObject(); json.put("uuid", uuid); json.put("event", Character.toString(event)); json.put("cpuTime", cpuTime); client.write((json + "\n").getBytes(UTF_8)); } @Override public void connectionOpened (NIOSocket socket) { SandboxMain.getLogger().info("Mit dem Worker verbunden"); connected = true; JSONArray langs = new JSONArray(commands.keySet().toArray()); client.write(("S" + langs + "\n").getBytes(UTF_8)); } private final Runnable reconnector = () -> { int interval = PropertyUtils.getInt(RECON_IVAL, 3000); while (!connected) { try { client = NetworkService.getService().openSocket(host, port); client.listen(this); } catch (IOException e) { Airbrake.log(e).printStackTrace(); } try { Thread.sleep(interval); } catch (InterruptedException e) { e.printStackTrace(); } } reconnectorRunning = false; }; private boolean reconnectorRunning = false; @Override public void connectionBroken (NIOSocket socket, Exception e) { SandboxMain.getLogger().critical("Verbindung zum Worker kaputt" + (e == null ? "" : ": " + e)); connected = false; synchronized (this) { if (!reconnectorRunning) new Thread(reconnector, "Reconnector").start(); reconnectorRunning = true; } } @Override public void packetReceived (NIOSocket socket, byte[] packet) { SandboxMain.getLogger().debug("Packet empfangen"); buf.add(packet); byte line[]; while ((line = buf.readLine()) != null) { try { SandboxCommand cmd = Parsers.getSandbox().parse(line, SandboxCommand.class); switch (cmd.getCommand()) { case RUN_AI: SandboxMain.getLogger().info("Auftrag erhalten: Run AI " + cmd.getId() + "v" + cmd.getVersion() + " " + cmd.getUuid() + " " + cmd.getLang()); jobControl.addJob(new Job(cmd.getId(), cmd.getVersion(), cmd.getBoxid(), cmd.getLang(), cmd.getUuid(), cmd.getMaxRuntime())); break; case TERM_AI: SandboxMain.getLogger().info("Auftrag erhalten: Terminate AI " + cmd.getUuid()); jobControl.terminateJob(cmd.getUuid()); break; case KILL_AI: SandboxMain.getLogger().info("Auftrag erhalten: Kill AI " + cmd.getUuid()); jobControl.killJob(cmd.getUuid()); break; case CPU_TIME: SandboxMain.getLogger().info("Auftrag erhalten: CPU-Time herausfinden"); { if (jobControl.getCurrent() == null) { SandboxMain.getLogger().warning("Attempting to get time of an exited sandbox"); break; } long time = CpuTimer.getCpuTime(jobControl.getCurrent().getJob().getBoxid()); SandboxMain.getLogger().debug(time); sendMessage(jobControl.getCurrent().getJob().getUuid(), 'C', time); } break; default: SandboxMain.getLogger().debug("Also es wäre schön wenn ich den Befehl " + cmd + " verstehen würde"); } } catch (Exception e) { SandboxMain.getLogger().critical("Error while parsing: " + new String(line, UTF_8)); Airbrake.log(e).printStackTrace(); } } } @Override public void packetSent (NIOSocket arg0, Object arg1) { } }