diff --git a/MultiServer/pom.xml b/MultiServer/pom.xml
new file mode 100644
index 0000000..15881d9
--- /dev/null
+++ b/MultiServer/pom.xml
@@ -0,0 +1,78 @@
+
+
+ 4.0.0
+ ASys Multi server
+
+
+
+ DmitriyMX
+ mail@dmiriymx.ru
+
+
+
+
+ UTF-8
+ 1.8
+
+
+ asys
+ multiserver
+ 0.1
+ bundle
+
+
+
+ asys
+ api
+ 0.10
+
+
+ org.osgi
+ org.osgi.core
+ 6.0.0
+
+
+ org.apache.felix
+ org.apache.felix.gogo.runtime
+ 0.10.0
+
+
+ commons-io
+ commons-io
+ 2.5
+
+
+
+
+ ${project.groupId}.${project.artifactId}-${project.version}
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+ ${java.version}
+ ${java.version}
+ ${project.build.sourceEncoding}
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ 3.0.1
+ true
+
+
+ ${project.name}
+ ${project.groupId}.${project.artifactId}
+ asys.multiserver.Activator
+ asys.api, *
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MultiServer/src/main/java/asys/multiserver/Activator.java b/MultiServer/src/main/java/asys/multiserver/Activator.java
new file mode 100644
index 0000000..7637300
--- /dev/null
+++ b/MultiServer/src/main/java/asys/multiserver/Activator.java
@@ -0,0 +1,79 @@
+/*
+ * DmitriyMX
+ * 2016-08-15
+ */
+package asys.multiserver;
+
+import asys.api.BankObject;
+import asys.api.MinecraftServerFactory;
+import asys.api.ServerManager;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+import static asys.api.ASysUtils.*;
+
+public class Activator implements BundleActivator {
+ private ServiceTracker, MinecraftServerFactory> mcServerFactoryTracker;
+ private ServiceTracker, BankObject> bankObjectTracker;
+ private ServiceRegistration> commands, serverManager;
+ private MultiServer multiServer;
+
+ @Override
+ public void start(BundleContext bundleContext) throws Exception {
+ mcServerFactoryTracker = new ServiceTracker<>(bundleContext, MinecraftServerFactory.class.getName(), null);
+ mcServerFactoryTracker.open();
+ bankObjectTracker = new ServiceTracker<>(bundleContext, BankObject.class.getName(), null);
+ bankObjectTracker.open();
+
+ multiServer = new MultiServer(
+ loadProps(GetProperty(bundleContext, "asys.config.dir", "conf")),
+ mcServerFactoryTracker);
+ multiServer.loadState(bankObjectTracker.getService());
+ serverManager = bundleContext.registerService(ServerManager.class.getName(), multiServer, null);
+
+ commands = RegisterCommands(bundleContext, new Commands(multiServer), "asys.server");
+ }
+
+ @Override
+ public void stop(BundleContext bundleContext) throws Exception {
+ commands.unregister();
+ serverManager.unregister();
+ multiServer.saveState(bankObjectTracker.getService());
+ multiServer = null;
+ bankObjectTracker.close();
+ mcServerFactoryTracker.close();
+ }
+
+ private Properties loadProps(String confDir) {
+ Properties properties = new Properties();
+
+ final String propsFileName = "asys-multiserver.properties";
+ Path propsPath = Paths.get(confDir).resolve(propsFileName);
+ if (Files.notExists(propsPath)) {
+ try {
+ SaveResource(Activator.class.getResourceAsStream("/"+propsFileName), propsPath.toFile());
+ } catch (IOException e) {
+ e.printStackTrace();
+ return properties;
+ }
+ }
+
+ try {
+ properties.load(new FileReader(propsPath.toFile()));
+ return properties;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return properties;
+ }
+}
diff --git a/MultiServer/src/main/java/asys/multiserver/Commands.java b/MultiServer/src/main/java/asys/multiserver/Commands.java
new file mode 100644
index 0000000..419acc6
--- /dev/null
+++ b/MultiServer/src/main/java/asys/multiserver/Commands.java
@@ -0,0 +1,165 @@
+/*
+ * DmitriyMX
+ * 2016-08-15
+ */
+package asys.multiserver;
+
+import asys.api.ASysUtils;
+import asys.api.Command;
+import asys.api.MinecraftServer;
+import org.apache.felix.service.command.Descriptor;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.StringJoiner;
+
+public class Commands {
+ private MultiServer multiServer;
+
+ public Commands(MultiServer multiServer) {
+ this.multiServer = multiServer;
+ }
+
+ @Command
+ @Descriptor("Распечатать текущие настройки модуля")
+ public void config() {
+ ASysUtils.Log("------ Config ------");
+ ASysUtils.Log("%-20s %s", "BuildScript folder:", multiServer.buildScriptPath.toAbsolutePath());
+ ASysUtils.Log("%-20s %s", "Distributive folder:", multiServer.distrPath.toAbsolutePath());
+ ASysUtils.Log("%-20s %s", "Servers folder:", multiServer.serversPath.toAbsolutePath());
+ }
+
+ @Command
+ @Descriptor("Развернуть новый сервер")
+ public void deploy(@Descriptor("тип сервера") String type) {
+ try {
+ ASysUtils.Log("New server id: %s", multiServer.deployServer(type));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Command
+ @Descriptor("Развернуть новый сервер")
+ public void deploy(@Descriptor("тип сервера") String type, @Descriptor("количество") int count) {
+ for (int i = 0; i < count; i++) {
+ try {
+ ASysUtils.Log("New server id: %s", multiServer.deployServer(type));
+ } catch (IOException e) {
+ e.printStackTrace();
+ break;
+ }
+ }
+ }
+
+ @Command
+ @Descriptor("Получить список серверов")
+ public void list() {
+ ASysUtils.Log("------ List servers ------");
+ final String format = " %-9s | %-6s";
+ ASysUtils.Log("%10s | %-6s", "ID", "STATUS");
+ multiServer.listServers().forEach(mcServer -> ASysUtils.Log(
+ format,
+ mcServer.getName(),
+ mcServer.isAlive() ? "Active" : "Ready"));
+ }
+
+ @Command
+ @Descriptor("Получить список серверов")
+ public void list(@Descriptor("статус") String status) {
+ ASysUtils.Log("------ List servers ------");
+ final String format = " %-9s | %-6s";
+ ASysUtils.Log("%10s | %-6s", "ID", "STATUS");
+ multiServer.listServers().stream()
+ .filter(mcServer -> ((status.equalsIgnoreCase("active") && mcServer.isAlive()) ||
+ (status.equalsIgnoreCase("ready") && !mcServer.isAlive())))
+ .forEach(mcServer -> ASysUtils.Log(
+ format,
+ mcServer.getName(),
+ mcServer.isAlive() ? "Active" : "Ready"));
+ }
+
+ @Command
+ @Descriptor("Получить список типов серверов")
+ public void types() {
+ ASysUtils.Log("------ Type servers ------");
+ multiServer.getTypes().forEach(type -> ASysUtils.Log(" %s", type));
+ }
+
+ @Command
+ @Descriptor("Старт сервера")
+ public void start(@Descriptor("id сервера") String serverId) {
+ MinecraftServer server = multiServer.getServer(serverId);
+ if (server == null) {
+ ASysUtils.Log("Server \"%s\" not found", serverId);
+ } else if (!server.isAlive()) {
+ server.start();
+ ASysUtils.Log("Server \"%s\" started", serverId);
+ }
+ }
+
+ @Command
+ @Descriptor("Остановка сервера")
+ public void stop(@Descriptor("id сервера") String serverId) {
+ MinecraftServer server = multiServer.getServer(serverId);
+ if (server == null) {
+ ASysUtils.Log("Server \"%s\" not found", serverId);
+ } else if (server.isAlive()) {
+ server.stop();
+ ASysUtils.Log("Server \"%s\" stoppind", serverId);
+ }
+ }
+
+ @Command
+ @Descriptor("Убить процесс сервера")
+ public void kill(@Descriptor("id сервера") String serverId) {
+ MinecraftServer server = multiServer.getServer(serverId);
+ if (server == null) {
+ ASysUtils.Log("Server \"%s\" not found", serverId);
+ } else if (server.isAlive()) {
+ server.forceStop();
+ ASysUtils.Log("Server \"%s\" killing", serverId);
+ }
+ }
+
+ @Command
+ @Descriptor("Отправить на сервер комманду")
+ public void cmd(@Descriptor("id сервера") String serverId, @Descriptor("коменда") String... command) {
+ MinecraftServer server = multiServer.getServer(serverId);
+ if (server == null) {
+ ASysUtils.Log("Server \"%s\" not found", serverId);
+ } else if (server.isAlive()) {
+ StringJoiner sj = new StringJoiner(" ");
+ Arrays.asList(command).forEach(sj::add);
+ server.sendCommand(sj.toString());
+ }
+ }
+
+// @Command
+ @Descriptor("Получить информацию о сервере")
+ public void info(@Descriptor("id сервера") String serverId) {
+ /*TODO вывести информацию о соответствующем сервере
+ * информация должна содержать следующее:
+ * - id сервера
+ * - текущий онлайн
+ * - максимальный онлайн
+ * - TPS
+ * - политику доступа
+ * - время uptime
+ * - использумое кол-во оперативной памяти
+ * - максимально доступное кол-во памяти
+ */
+ }
+
+// @Command
+ @Descriptor("Установить серверу политику доступа")
+ public void accessPolicy(@Descriptor("id сервера") String serverid, String newPolicy) {
+ //TODO устанавливает соответствующему серверу политику доступа
+ }
+
+// @Command
+ @Descriptor("Удалить сервер (физически)")
+ public void remove(@Descriptor("id сервера") String serverId) {
+ //TODO удаляет сервер с диска
+ }
+}
diff --git a/MultiServer/src/main/java/asys/multiserver/MultiServer.java b/MultiServer/src/main/java/asys/multiserver/MultiServer.java
new file mode 100644
index 0000000..843fba6
--- /dev/null
+++ b/MultiServer/src/main/java/asys/multiserver/MultiServer.java
@@ -0,0 +1,185 @@
+/*
+ * DmitriyMX
+ * 2016-08-15
+ */
+package asys.multiserver;
+
+import asys.api.BankObject;
+import asys.api.MinecraftServer;
+import asys.api.MinecraftServerFactory;
+import asys.api.ServerManager;
+import asys.multiserver.buildscript.BuildScript;
+import asys.multiserver.buildscript.CommandException;
+import org.osgi.util.tracker.ServiceTracker;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+
+public class MultiServer implements ServerManager {
+ private Map mapMcServers = new HashMap<>();
+ private Random random = new Random(System.currentTimeMillis());
+ private ServiceTracker, MinecraftServerFactory> mcsfTracker;
+ Path buildScriptPath, distrPath, serversPath;
+
+ public MultiServer(Properties properties, ServiceTracker, MinecraftServerFactory> mcServerFactoryTracker) {
+ buildScriptPath = Paths.get(properties.getProperty("buildscript.dir", "scripts"));
+ distrPath = Paths.get(properties.getProperty("distributive.dir", "distr"));
+ serversPath = Paths.get(properties.getProperty("servers.dir", "servers"));
+ this.mcsfTracker = mcServerFactoryTracker;
+
+ File[] serverDirs = serversPath.toFile().listFiles((dir, name) -> dir.isDirectory());
+ if (serverDirs != null) {
+ for(File serverDir :serverDirs) {
+ Path asysPropsPath = serverDir.toPath().resolve("asys.properties");
+ if (Files.exists(asysPropsPath)) {
+ Properties asysProps = new Properties();
+ try {
+ FileReader fileReader = new FileReader(asysPropsPath.toFile());
+ asysProps.load(fileReader);
+ fileReader.close();
+ } catch (IOException e) {
+// e.printStackTrace();
+ return;
+ }
+
+ putServer(asysProps, serverDir);
+ }
+ }
+ }
+ }
+
+ public List getTypes() {
+ List list = new ArrayList<>();
+ String[] files = buildScriptPath.toFile().list((dir, name) -> name.indexOf(' ') == -1 && name.endsWith(".bs"));
+ if (files != null) {
+ Arrays.stream(files).map(s -> {
+ int i = s.lastIndexOf(".bs");
+ return s.substring(0, i);
+ }).forEach(list::add);
+ }
+
+ return list;
+ }
+
+ public String deployServer(String type) throws IOException {
+ String serverId;
+ Path newServerPath;
+ final int max = 99;
+ int _try = 0;
+ int nId;
+ do {
+ if (_try == max*2) throw new IOException("End of free server id");
+ nId = random.nextInt(max);
+ serverId = type + nId;
+ newServerPath = serversPath.resolve(serverId);
+ _try++;
+ } while (Files.exists(newServerPath));
+
+ Files.createDirectory(newServerPath);
+
+ try {
+ BuildScript buildScript = BuildScript.loadFromFile(buildScriptPath.resolve(type + ".bs").toFile());
+ buildScript.setVariable("servers", serversPath.toAbsolutePath().toString());
+ buildScript.setVariable("distrib", distrPath.toAbsolutePath().toString());
+ buildScript.setVariable("serverId", serverId);
+ buildScript.execute();
+ } catch (CommandException e) {
+ throw new IOException(e);
+ }
+
+ Properties asysProps = new Properties();
+ Path serverDirPath = serversPath.resolve(serverId);
+ Path asysPropPath = serverDirPath.resolve("asys.properties");
+ if (Files.exists(asysPropPath)) {
+ asysProps.load(new FileReader(asysPropPath.toFile()));
+ }
+ asysProps.setProperty("server.id", serverId);
+ if (asysProps.getProperty("server.mainjar", null) == null) {
+ asysProps.setProperty("server.mainjar", "spigot.jar");
+ }
+ if (asysProps.getProperty("server.port.prefix", null) == null) {
+ asysProps.setProperty("server.port.prefix", "00");
+ }
+ asysProps.setProperty("server.port",
+ "2"+asysProps.getProperty("server.port.prefix")+nId);
+ asysProps.store(new FileWriter(asysPropPath.toFile()), "ASys Server settings");
+
+ putServer(asysProps, serverDirPath.toFile());
+
+ return serverId;
+ }
+
+ private void putServer(Properties asysProps, File serverDir) {
+ if (asysProps.getProperty("server.id", null) == null)
+ return; //TODO ошибку бы генерировать
+ if (asysProps.getProperty("server.port", null) == null)
+ return; //TODO ошибку бы генерировать
+
+ MinecraftServerFactory serverFactory = null;
+ try {
+ serverFactory = mcsfTracker.waitForService(1000L);
+ } catch (InterruptedException e) {
+ }
+
+ if (serverFactory == null)
+ return; //TODO ошибку бы генерировать
+
+ mapMcServers.put(
+ asysProps.getProperty("server.id"),
+ serverFactory.createServer(
+ asysProps.getProperty("server.id"),
+ serverDir,
+ asysProps.getProperty("server.mainjar"),
+ Short.valueOf(asysProps.getProperty("server.port")), //TODO надо сделать защиту от дурака: что если буквы введут?
+ asysProps.getProperty("server.jvm.args"),
+ asysProps.getProperty("server.params")
+ ));
+ }
+
+ public List listServers() {
+ return new ArrayList<>(mapMcServers.values());
+ }
+
+ @Override
+ public MinecraftServer getServer(String serverId) {
+ return mapMcServers.get(serverId); //TODO по хорошему, надо бы возвращать какой-нибудь EmptyPbject, а не null
+ }
+
+ @Override
+ public void removeServer(String serverId) {
+ mapMcServers.remove(serverId);
+ }
+
+ @SuppressWarnings("unchecked")
+ void loadState(BankObject bankObject) {
+ if(bankObject == null) return;
+
+ List serversState = (List) bankObject.get(MultiServer.class.getName()+"#servers");
+ if (serversState == null) return;
+
+ serversState.forEach(server -> {
+ if (server.isAlive()) {
+ mapMcServers.put(server.getName(), server);
+ }
+ });
+ }
+
+ void saveState(BankObject bankObject) {
+ if(bankObject == null) return;
+
+ List serversState = new ArrayList<>();
+ mapMcServers.values().forEach(server -> {
+ if (server.isAlive()) {
+ serversState.add(server);
+ }
+ });
+
+ bankObject.save(MultiServer.class.getName()+"#servers", serversState);
+ }
+}
diff --git a/MultiServer/src/main/java/asys/multiserver/buildscript/BuildScript.java b/MultiServer/src/main/java/asys/multiserver/buildscript/BuildScript.java
new file mode 100644
index 0000000..cb68c12
--- /dev/null
+++ b/MultiServer/src/main/java/asys/multiserver/buildscript/BuildScript.java
@@ -0,0 +1,277 @@
+/*
+ * DmitriyMX
+ * 2016-06-23
+ */
+package asys.multiserver.buildscript;
+
+import asys.api.ASysUtils;
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * Скрипт формирования окружения игрового сервера.
+ *
+ * Команды:
+ * - lnk [источник] [цель] - создание ссылки;
+ * - copy [источник] [цель] - копирование;
+ * - mkdir [путь] - создание папки или древа папок;
+ * - unpack [архив] [папка] - распаковка архива в указанную папку;
+ * - undoparent - отменить запланированные действия вышестоящего скрипта.
+ */
+public class BuildScript {
+ List scriptLines = new ArrayList<>();
+ private Map variables = new HashMap<>();
+
+ public static BuildScript loadFromFile(File file) throws IOException, UnknowCommandException {
+ return new BuildScript(FileUtils.readFileToString(file, Charset.forName("UTF-8")));
+ }
+
+ public BuildScript(String script) throws UnknowCommandException {
+ if (script == null || script.trim().isEmpty()) {
+ ASysUtils.Log("[WARN] Empty script!");
+ return;
+ }
+
+ if (script.contains("\r\n")) {
+// ASysUtils.Log("[WARN] Finded CRLF! Replaced...");
+ script = script.replaceAll("\r\n", "\n");
+ }
+
+ for (String line : script.split("\n")) {
+ scriptLines.add(parseCommandLine(line));
+ }
+ }
+
+ public void setVariables(Map variables) {
+ this.variables = variables;
+ }
+
+ public void setVariable(String name, String value) {
+ this.variables.put(name, value);
+ }
+
+ private String[] parseCommandLine(String line) throws UnknowCommandException {
+ final StringTokenizer strTok = new StringTokenizer(line, " \"\'", true);
+ final List preArr = new ArrayList<>();
+ byte state = 0; // 0-normal, 1-quoting1, 2-quoting2
+ String buff = "";
+ boolean foundCmd = false;
+
+ while (strTok.hasMoreTokens()) {
+ String partLine = strTok.nextToken();
+
+ if (!foundCmd) {
+ switch (partLine) {
+ case "lnk":
+ case "copy":
+ case "mkdir":
+ case "unpack":
+ case "undoparent":
+ foundCmd = true; // cmd correct;
+ partLine = partLine.toLowerCase();
+ break;
+ default:
+ foundCmd = false; // cmd correct;
+ }
+
+ if (!foundCmd) {
+ throw new UnknowCommandException(partLine);
+ }
+ }
+
+ if (partLine.equals("\"")) {
+ if (state == 0) {
+ state = 1;
+ continue;
+ } else if (state == 1){
+ state = 0;
+ preArr.add(buff);
+ buff = "";
+ continue;
+ }
+ }
+
+ if (partLine.equals("\'")) {
+ if (state == 0) {
+ state = 2;
+ continue;
+ } else if (state == 2){
+ state = 0;
+ preArr.add(buff);
+ buff = "";
+ continue;
+ }
+ }
+
+ if (state == 1 || state == 2) {
+ buff += partLine;
+ continue;
+ }
+
+ if (partLine.equals(" ")) continue;
+
+ preArr.add(partLine);
+ }
+
+ return preArr.toArray(new String[preArr.size()]);
+ }
+
+ public void execute() throws CommandException {
+ for (String[] cmdline : scriptLines) {
+ switch (cmdline[0]) {
+ case "lnk": cmd_lnk(cmdline[1], cmdline[2]); break;
+ case "copy": cmd_copy(cmdline[1], cmdline[2]); break;
+ case "mkdir": cmd_mkdir(cmdline[1]); break;
+ case "unpack": cmd_unpack(cmdline[1], cmdline[2]); break;
+ case "undoparent": cmd_undo(); break;
+ default: return;
+ }
+ }
+ }
+
+ private String applyVariables(String string) {
+ if (variables != null && !variables.isEmpty()) {
+ for (Map.Entry entry : variables.entrySet()) {
+ string = string.replace("%"+entry.getKey()+"%", entry.getValue());
+ }
+ }
+
+ return string;
+ }
+
+ /* Создание символьной ссылки */
+ private void cmd_lnk(String source, String target) throws CommandException {
+ try {
+ Files.createSymbolicLink(
+ Paths.get(applyVariables(target)),
+ Paths.get(applyVariables(source)));
+ } catch (IOException e) {
+ throw new CommandException("LNK", e);
+ }
+ }
+
+ /* Коирование */
+ private void cmd_copy(String source, String target) throws CommandException {
+ //TODO надо изюавиться от излишних Path.toFile()
+ Path sourcePath = Paths.get(applyVariables(source));
+ if (!sourcePath.toFile().exists()) {
+ throw new CommandException(String.format("COPY: source not found %s [%s]", source, sourcePath.toAbsolutePath()));
+ }
+
+ Path targetPath = Paths.get(applyVariables(target));
+ if (!targetPath.toFile().exists()) {
+ if (sourcePath.toFile().isDirectory()) {
+ try {
+ FileUtils.copyDirectory(sourcePath.toFile(), targetPath.toFile());
+ } catch (IOException e) {
+ throw new CommandException("COPY: error copy directory", e);
+ }
+ } else if (sourcePath.toFile().isFile()) {
+ try {
+ FileUtils.copyFile(sourcePath.toFile(), targetPath.toFile());
+ } catch (IOException e) {
+ throw new CommandException("COPY: error copy file", e);
+ }
+ } else {
+ throw new CommandException("COPY: unknow type source file");
+ }
+ } else {
+ if (sourcePath.toFile().isDirectory()) {
+ if (targetPath.toFile().isFile()) {
+ throw new CommandException("COPY: can't be copy dir to file");
+ } else {
+ try {
+ FileUtils.copyDirectory(sourcePath.toFile(), targetPath.toFile());
+ } catch (IOException e) {
+ throw new CommandException("COPY: error copy directory", e);
+ }
+ }
+ } else if (sourcePath.toFile().isFile()) {
+ if (targetPath.toFile().isDirectory()) {
+ try {
+ FileUtils.copyFileToDirectory(sourcePath.toFile(), targetPath.toFile());
+ } catch (IOException e) {
+ throw new CommandException("COPY: error copy file", e);
+ }
+ } else if (targetPath.toFile().isFile()) {
+ try {
+ FileUtils.copyFile(sourcePath.toFile(), targetPath.toFile());
+ } catch (IOException e) {
+ throw new CommandException("COPY: error copy file", e);
+ }
+ }
+ }
+ }
+ }
+
+ /* Создание папки/древа папок
+ * параметры: цель */
+ private void cmd_mkdir(String dir) throws CommandException {
+ File treeDir = Paths.get(applyVariables(dir)).toFile();
+ if (treeDir.mkdirs() && !treeDir.exists()) {
+ throw new CommandException(String.format("MKDIR: can't create dirs %s [%s]", dir, treeDir.getAbsolutePath()));
+ }
+ }
+
+ private void cmd_unpack(String source, String target) throws CommandException {
+ Path sourcePath = Paths.get(applyVariables(source));
+ if (!sourcePath.toFile().exists()) {
+ throw new CommandException(String.format("UNPACK: source not found %s [%s]", source, sourcePath.toFile().getAbsolutePath()));
+ }
+
+ if (!sourcePath.toFile().isFile()) {
+ throw new CommandException(String.format("UNPACK: source is not file %s [%s]", source, sourcePath.toFile().getAbsolutePath()));
+ }
+
+ Path targetPath = Paths.get(applyVariables(target));
+ if (!targetPath.toFile().exists()) {
+ if (targetPath.toFile().mkdirs() && !targetPath.toFile().exists()) {
+ throw new CommandException(String.format("UNPACK: can't create dir %s [%s]", target, targetPath.toFile().getAbsolutePath()));
+ }
+ } else if (targetPath.toFile().exists() && targetPath.toFile().isFile()) {
+ throw new CommandException(String.format("UNPACK: target can't be file %s [%s]", target, targetPath.toFile().getAbsolutePath()));
+ }
+
+ // unzip
+ byte[] buffer = new byte[65536];
+ try {
+ ZipInputStream zis = new ZipInputStream(new FileInputStream(sourcePath.toFile()));
+
+ ZipEntry ze;
+ while ((ze = zis.getNextEntry()) != null) {
+ if (ze.isDirectory()) continue;
+ String fileName = ze.getName();
+ File newFile = new File(targetPath.toFile(), fileName);
+ Files.createDirectories(Paths.get(newFile.getParent()));
+
+ FileOutputStream fos = new FileOutputStream(newFile);
+ int len;
+ while ((len = zis.read(buffer)) > 0) {
+ fos.write(buffer, 0, len);
+ }
+ fos.close();
+ }
+
+ zis.closeEntry();
+ zis.close();
+ } catch (IOException e) {
+ throw new CommandException("UNPACK", e);
+ }
+ }
+
+ private void cmd_undo() {
+ // empty
+ }
+}
+
diff --git a/MultiServer/src/main/java/asys/multiserver/buildscript/CommandException.java b/MultiServer/src/main/java/asys/multiserver/buildscript/CommandException.java
new file mode 100644
index 0000000..d7b3e57
--- /dev/null
+++ b/MultiServer/src/main/java/asys/multiserver/buildscript/CommandException.java
@@ -0,0 +1,15 @@
+/*
+ * DmitriyMX
+ * 2016-06-28
+ */
+package asys.multiserver.buildscript;
+
+public class CommandException extends Exception {
+ CommandException(String message) {
+ super(message);
+ }
+
+ CommandException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
\ No newline at end of file
diff --git a/MultiServer/src/main/java/asys/multiserver/buildscript/UnknowCommandException.java b/MultiServer/src/main/java/asys/multiserver/buildscript/UnknowCommandException.java
new file mode 100644
index 0000000..709806e
--- /dev/null
+++ b/MultiServer/src/main/java/asys/multiserver/buildscript/UnknowCommandException.java
@@ -0,0 +1,11 @@
+/*
+ * DmitriyMX
+ * 2016-06-23
+ */
+package asys.multiserver.buildscript;
+
+public class UnknowCommandException extends CommandException {
+ UnknowCommandException(String message) {
+ super(message);
+ }
+}
diff --git a/MultiServer/src/main/resources/asys-multiserver.properties b/MultiServer/src/main/resources/asys-multiserver.properties
new file mode 100644
index 0000000..88114c0
--- /dev/null
+++ b/MultiServer/src/main/resources/asys-multiserver.properties
@@ -0,0 +1,3 @@
+buildscript.dir=scripts
+distributive.dir=distr
+servers.dir=servers
\ No newline at end of file