/* * DatastoreFtpClient.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.networking; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import org.pixelgaffer.turnierserver.Logger; import it.sauronsoftware.ftp4j.FTPAbortedException; import it.sauronsoftware.ftp4j.FTPClient; import it.sauronsoftware.ftp4j.FTPDataTransferException; import it.sauronsoftware.ftp4j.FTPException; import it.sauronsoftware.ftp4j.FTPFile; import it.sauronsoftware.ftp4j.FTPIllegalReplyException; import it.sauronsoftware.ftp4j.FTPListParseException; import lombok.AccessLevel; import lombok.NoArgsConstructor; /** * Diese Klasse verbindet sich zum FTP Server auf dem Datastore und enthält * Methoden zum Abrufen der Daten. */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class DatastoreFtpClient { private static FtpConnectionPool cons = new FtpConnectionPool(); /** * Verbindet mit dem Server mithilfe der Systemproperties. */ static FTPClient connect (FTPClient client) throws IOException, FTPIllegalReplyException, FTPException { if (client != null) { try { client.noop(); } catch (IllegalStateException | IOException | FTPIllegalReplyException | FTPException e) { try { client.disconnect(true); } catch (Exception ex) { } client = null; } catch (Exception e) { } } if (client == null) client = new FTPClient(); if (!client.isConnected()) { // zum Server verbinden client.connect(System.getProperty("turnierserver.datastore.host"), Integer.parseInt(System.getProperty("turnierserver.datastore.port"))); // beim Server anmelden new Logger().debug("Melde mich beim FTP-Server an"); String username = System.getProperty("turnierserver.datastore.username"); String password = System.getProperty("turnierserver.datastore.password"); client.login(username, password); // passiven Modus benutzen (Firewall …) client.setPassive(true); } return client; } private static String aiPath (int id) { return "AIs/" + id; } private static String aiBinPath (int id) { return aiPath(id) + "/bin"; } private static String aiSourcePath (int id, int version) { return aiPath(id) + "/v" + version; } /** * Speichert den Remote-File im OutputStream local. */ private static void retrieveFile (String remote, OutputStream local, FtpConnection con) throws IOException, FTPIllegalReplyException, FTPException, IllegalStateException, FTPDataTransferException, FTPAbortedException { boolean conWasNull = con == null; if (conWasNull) con = cons.getClient(); FTPClient client = con.getFtpClient(); try { client.download(remote, local, 0, null); } finally { if (conWasNull) con.setBusy(false); } local.close(); } /** * Speichert den Remote-File in local. */ private static void retrieveFile (String remote, File local, FtpConnection con) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException { boolean conWasNull = con == null; if (conWasNull) con = cons.getClient(); FTPClient client = con.getFtpClient(); try { client.download(remote, local); } finally { if (conWasNull) con.setBusy(false); } } /** * Speichert alles im Remote-Directory im local-Verzeichnis. */ public static void retrieveDir (String remote, File local, FtpConnection con) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException, FTPListParseException { boolean conWasNull = con == null; if (conWasNull) con = cons.getClient(); FTPClient client = con.getFtpClient(); try { String cwd = client.currentDirectory(); client.changeDirectory(remote); local.mkdirs(); for (FTPFile f : client.list()) { if (f.getType() == FTPFile.TYPE_FILE) retrieveFile(f.getName(), new File(local, f.getName()), con); else if (f.getType() == FTPFile.TYPE_DIRECTORY) retrieveDir(f.getName(), new File(local, f.getName()), con); } client.changeDirectory(cwd); } finally { if (conWasNull) con.setBusy(false); } } /** * Speichert das tar-bzip2-Archiv mit den compilierten Daten der KI im * OutputStream local. */ public static void retrieveAi (int id, int version, OutputStream local) throws IllegalStateException, IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException { retrieveFile(aiBinPath(id) + "/v" + version + ".tar.bz2", local, null); } /** * Speichert das tar-bzip2-Archiv mit den compilierten Daten der KI in * local. */ public static void retrieveAi (int id, int version, File local) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException { retrieveFile(aiBinPath(id) + "/v" + version + ".tar.bz2", local, null); } /** * Speichert alle Dateien im Source-Folder der KI in einem temporären * Verzeichnis und gibt dieses zurück. */ public static File retrieveAiSource (int id, int version) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException, FTPListParseException { File tmp = Files.createTempDirectory("ai").toFile(); retrieveDir(aiSourcePath(id, version), tmp, null); return tmp; } /** * Speichert das jar-Archiv der Gamelogic im OutputStream local. */ public static void retrieveGameLogic (int game, File local) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException { retrieveFile("Games/" + game + "/Logic.jar", local, null); } /** * Speichert die AiLibrary der angegebenen Sprache im Verzeichnis local. */ public static void retrieveAiLibrary (int game, String language, File local) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException, FTPListParseException { retrieveDir("Games/" + game + "/" + language + "/ailib", local, null); } /** * Speichert die jar-Archive der Bibliothek im Verzeichnis local. */ public static void retrieveLibrary (String lib, String language, File local) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException, FTPListParseException { retrieveDir("Libraries/" + language + "/" + lib, local, null); } /** * Lädt die KIs des Turniers herunter. */ public static String retrieveTournamentAis (int id) throws IOException, FTPIllegalReplyException, FTPException, IllegalStateException, FTPDataTransferException, FTPAbortedException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); retrieveFile("Tournaments/" + id + "/ais.json", baos, null); return new String(baos.toByteArray(), UTF_8); } /** * Lädt den Inhalt des InputStreams local nach remote. */ private static void storeFile (String remote, InputStream local, FtpConnection con) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException { boolean conWasNull = con == null; if (conWasNull) con = cons.getClient(); FTPClient client = con.getFtpClient(); try { client.upload(remote, local, 0, 0, null); } finally { if (conWasNull) con.setBusy(false); } local.close(); } /** * Lädt den Inhalt des InputStreams local als Binary-tar-bz2-Archiv der KI * hoch. */ public static void storeAi (int ai, int version, InputStream local) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException { storeFile(aiBinPath(ai) + "/v" + version + ".tar.bz2", local, null); } /** * Lädt die Ausgabe des Kompilierungsprozess der KI hoch. */ public static void storeAiCompileOutput (int aiId, int version, File local) throws IOException, FTPIllegalReplyException, FTPException, FTPDataTransferException, FTPAbortedException { storeFile(aiBinPath(aiId) + "/v1-compile.out", new FileInputStream(local), null); } /** * Gibt die Größe der angegebenen Datei zurück. */ private static long fileSize (String remote, FtpConnection con) throws IOException, FTPIllegalReplyException, FTPException { boolean conWasNull = con == null; if (conWasNull) con = cons.getClient(); FTPClient client = con.getFtpClient(); long retval; try { retval = client.fileSize(remote); } finally { if (conWasNull) con.setBusy(false); } return retval; } /** * Gibt die Größe des tar-bzip2-Archivs mit den compilierten Daten der KI * zurück. */ public static long aiSize (int id, int version) throws IOException, FTPIllegalReplyException, FTPException { return fileSize(aiBinPath(id) + "/v" + version + ".tar.bz2", null); } }