/*
* WorkerConnectionHandler.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.worker.server;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.pixelgaffer.turnierserver.networking.messages.WorkerConnectionType.AI;
import static org.pixelgaffer.turnierserver.networking.messages.WorkerConnectionType.BACKEND;
import static org.pixelgaffer.turnierserver.networking.messages.WorkerConnectionType.SANDBOX;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.pixelgaffer.turnierserver.Airbrake;
import org.pixelgaffer.turnierserver.Parsers;
import org.pixelgaffer.turnierserver.networking.ConnectionHandler;
import org.pixelgaffer.turnierserver.networking.bwprotocol.AiConnected;
import org.pixelgaffer.turnierserver.networking.messages.MessageForward;
import org.pixelgaffer.turnierserver.networking.messages.SandboxCommand;
import org.pixelgaffer.turnierserver.networking.messages.SandboxMessage;
import org.pixelgaffer.turnierserver.networking.messages.WorkerConnectionType;
import org.pixelgaffer.turnierserver.networking.util.DataBuffer;
import org.pixelgaffer.turnierserver.worker.Sandbox;
import org.pixelgaffer.turnierserver.worker.Sandboxes;
import org.pixelgaffer.turnierserver.worker.WorkerMain;
import lombok.Getter;
import lombok.ToString;
import naga.NIOSocket;
@ToString(of = { "type" })
public class WorkerConnectionHandler extends ConnectionHandler
{
/** Der lokale Buffer mit den noch nicht gelesenen bytes. */
private DataBuffer buffer = new DataBuffer();
/** Informationen zum Typ dieser Connection. */
@Getter
private WorkerConnectionType type;
/**
* Sollte es sich um eine Sandbox-Connection handeln, ist dies das
* zugehörige Sandbox-Objekt.
*/
private Sandbox sandbox;
public WorkerConnectionHandler (NIOSocket socket)
{
super(socket);
}
/**
* Schickt den Job an den Client, sollte eine Sandbox sein.
*/
public synchronized void sendJob (SandboxCommand job) throws IOException
{
if (type.getType() != SANDBOX)
WorkerMain.getLogger().warning("Schicke Job an " + type.getType() + " (sollte " + SANDBOX + " sein)");
getClient().write(Parsers.getSandbox().parse(job, true));
}
/**
* Schickt die Message an den Client, sollte eine KI sein.
*/
public synchronized void sendMessage (MessageForward mf)
{
if (type.getType() != AI)
WorkerMain.getLogger().warning("Schicke Nachricht an " + type.getType() + " (sollte " + AI + " sein)");
new Thread(() -> {
WorkerMain.getLogger().debug("Will message " + new String(mf.getMessage(), UTF_8) + " forwarden");
try {
Sandbox sandbox = Sandboxes.sandboxJobs.get(mf.getAi());
sandbox.updateCpuTime();
}
catch(Exception e) {
e.printStackTrace();
return;
}
WorkerMain.getLogger().debug("Werde nun " + new String(mf.getMessage(), UTF_8) + " forwarden");
getClient().write(mf.getMessage());
WorkerMain.getLogger().debug("Habe message " + new String(mf.getMessage(), UTF_8) + " forwarden");
}).start();
//getClient().write("\n".getBytes(UTF_8));
}
@Override
public void disconnected ()
{
if (type != null)
{
WorkerMain.getLogger().info(type + (type.getType() == SANDBOX ? " (" + sandbox + ")" : "")
+ " hat die Verbindung getrennt.");
switch (type.getType())
{
case AI:
WorkerServer.aiConnections.remove(type.getUuid());
break;
case BACKEND:
WorkerServer.backendConnection = null;
break;
case SANDBOX:
Sandboxes.removeSandbox(sandbox);
break;
}
}
else
WorkerMain.getLogger().info(getClient().getIp() + " hat sich disconnected bevor er irgendwas gesendet hat");
}
@Override
public void packetReceived (NIOSocket socket, byte[] packet)
{
buffer.add(packet);
byte line[];
while ((line = buffer.readLine()) != null)
{
byte _line[] = line;
//WorkerMain.getLogger().debug("Empfangen: " + new String(packet, UTF_8));
// new Thread( () -> {
// wenn type noch null ist, diesen lesen
if (type == null)
{
String linestr = new String(_line, UTF_8);
type = WorkerConnectionType.parse(linestr);
if (type == null)
{
WorkerMain.getLogger()
.critical("Kann WorkerConnectionType nicht aus " + linestr + " lesen");
socket.close();
return;
}
switch (type.getType())
{
case AI:
WorkerMain.getLogger().info("Die KI " + type.getUuid() + " hat sich verbunden");
WorkerServer.aiConnections.put(type.getUuid(), this);
try
{
WorkerMain.getBackendClient().sendAiConnected(new AiConnected(type.getUuid()));
}
catch (IOException e)
{
e.printStackTrace();
}
break;
case BACKEND:
WorkerServer.backendConnection = this;
WorkerMain.getLogger().info("Das Backend hat sich verbunden");
break;
case SANDBOX:
sandbox = new Sandbox(this);
sandbox.setLangs(type.getLangs());
WorkerMain.getLogger().info("Eine neue Sandbox hat sich verbunden: " + sandbox);
Sandboxes.addSandbox(sandbox);
break;
}
return;
}
switch (type.getType())
{
case AI:
// von einer AI kommende Pakete zum Backend
// weiterleiten
if ((WorkerServer.backendConnection != null)
&& WorkerServer.backendConnection.isConnected())
{
new Thread(() -> {
try
{
ByteArrayOutputStream message = new ByteArrayOutputStream();
long cpuDiff = Sandboxes.sandboxJobs.get(type.getUuid()).getCpuTimeDiff();
long cpuDiffMikros = Math.round(cpuDiff / 1000.0);
String longString = Long.toString(cpuDiffMikros);
message.write(longString.getBytes(UTF_8));
message.write("|".getBytes(UTF_8));
message.write(_line);
WorkerMain.getLogger().debug("Forwarde von KI zu Backend: " + new String(message.toByteArray(), UTF_8));
MessageForward mf = new MessageForward(type.getUuid(), message.toByteArray());
DataBuffer buf = new DataBuffer();
buf.add(Parsers.getWorker().parse(mf, true));
WorkerServer.backendConnection.getClient().write(buf.readAll());
}
catch (Exception e)
{
WorkerMain.getLogger().critical("Fehler beim Weiterleiten: " + e);
Airbrake.log(e);
e.printStackTrace();
}
}).start();
}
else
WorkerMain.getLogger().critical("Habe keine Verbindung zum Backend gefunden");
break;
case BACKEND:
// vom Backend kommende Packete an die entsprechende
// KI
// weiterleiten
try
{
MessageForward mf = Parsers.getWorker().parse(_line, MessageForward.class);
WorkerConnectionHandler con = WorkerServer.aiConnections.get(mf.getAi());
if (con == null)
throw new IllegalArgumentException("Unbekannte KI mit der UUID " + mf.getAi());
con.sendMessage(mf);
}
catch (Exception e)
{
WorkerMain.getLogger().critical("Fehler beim Weiterleiten: " + e);
Airbrake.log(e);
e.printStackTrace();
}
break;
case SANDBOX:
try
{
SandboxMessage msg = Parsers.getSandbox().parse(_line, SandboxMessage.class);
sandbox.sandboxAnswer(msg);
}
catch (Exception e)
{
WorkerMain.getLogger().critical("Fehler beim Lesen der Nachricht der Sandbox: " + e);
Airbrake.log(e);
e.printStackTrace();
}
break;
}
// }).start();
}
}
}