diff --git a/core/pom.xml b/core/pom.xml index 943e84a..5126b7d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -16,6 +16,14 @@ ${asys.version} + + + commons-io + commons-io + 2.5 + + + ${project.groupId}.${project.artifactId}-${project.version} diff --git a/core/src/main/java/asys/core/buildscript/BuildScript.java b/core/src/main/java/asys/core/buildscript/BuildScript.java new file mode 100644 index 0000000..49454b9 --- /dev/null +++ b/core/src/main/java/asys/core/buildscript/BuildScript.java @@ -0,0 +1,268 @@ +/* + * DmitriyMX + * 2016-06-23 + */ +package asys.core.buildscript; + +import org.apache.commons.io.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * Скрипт формирования окружения игрового сервера.

+ * + * Команды:
+ * - lnk [источник] [цель] - создание ссылки;
+ * - copy [источник] [цель] - копирование;
+ * - mkdir [путь] - создание папки или древа папок;
+ * - unpack [архив] [папка] - распаковка архива в указанную папку;
+ * - undoparent - отменить запланированные действия вышестоящего скрипта. + */ +public class BuildScript { + private Logger logger = LoggerFactory.getLogger(BuildScript.class); + List scriptLines = new ArrayList<>(); + private Map variables; + + public BuildScript(String script) throws UnknowCommandException { + if (script == null || script.trim().isEmpty()) { + logger.warn("Empty script!"); + return; + } + + if (script.contains("\r\n")) { + logger.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; + } + + 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) { + 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/core/src/main/java/asys/core/buildscript/CommandException.java b/core/src/main/java/asys/core/buildscript/CommandException.java new file mode 100644 index 0000000..3ecef67 --- /dev/null +++ b/core/src/main/java/asys/core/buildscript/CommandException.java @@ -0,0 +1,15 @@ +/* + * DmitriyMX + * 2016-06-28 + */ +package asys.core.buildscript; + +class CommandException extends Exception { + CommandException(String message) { + super(message); + } + + CommandException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/core/src/main/java/asys/core/buildscript/UnknowCommandException.java b/core/src/main/java/asys/core/buildscript/UnknowCommandException.java new file mode 100644 index 0000000..81d8555 --- /dev/null +++ b/core/src/main/java/asys/core/buildscript/UnknowCommandException.java @@ -0,0 +1,11 @@ +/* + * DmitriyMX + * 2016-06-23 + */ +package asys.core.buildscript; + +class UnknowCommandException extends CommandException { + UnknowCommandException(String message) { + super(message); + } +} diff --git a/core/src/test/java/asys/core/buildscript/TestBuildScript.java b/core/src/test/java/asys/core/buildscript/TestBuildScript.java new file mode 100644 index 0000000..cbe3aa7 --- /dev/null +++ b/core/src/test/java/asys/core/buildscript/TestBuildScript.java @@ -0,0 +1,219 @@ +/* + * DmitriyMX + * 2016-06-23 + */ +package asys.core.buildscript; + +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import static org.junit.Assert.*; + +public class TestBuildScript { + private static File tempDirectory; + private Map variables = new HashMap<>(); + + @BeforeClass + public static void beforeTestClass() throws IOException { + tempDirectory = new File(FileUtils.getTempDirectory(), "test_buildscript"); + + if (tempDirectory.exists()) { + if (tempDirectory.isDirectory()) { + FileUtils.deleteDirectory(tempDirectory); + if (tempDirectory.exists()) { + throw new IOException(String.format("Can't delete temp directory [%s]", tempDirectory.getAbsolutePath())); + } + } else { + if (!tempDirectory.delete()) { + throw new IOException(String.format("Can't delete temp file [%s]", tempDirectory.getAbsolutePath())); + } + } + } + if (!tempDirectory.mkdir()) { + throw new IOException(String.format("Can't create temp dir [%s]", tempDirectory.getAbsolutePath())); + } + } + + @Before + public void beforeTest() throws IOException { + variables.clear(); + variables.put("dir", tempDirectory.getAbsolutePath()); + + File[] files = tempDirectory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + FileUtils.deleteDirectory(file); + assertFalse(file.exists()); + } else { + assertTrue(file.delete()); + } + } + } + } + + @Test + public void testParseCommandLine() throws UnknowCommandException { + BuildScript buildScript = new BuildScript("lnk \"%distr%/spigot 1.8.8.jar\" '%srv%/spi 1 got.jar'"); + String[] partsLine = buildScript.scriptLines.get(0); + + assertNotNull(partsLine); + assertEquals(3, partsLine.length); + assertEquals("lnk", partsLine[0]); + assertEquals("%distr%/spigot 1.8.8.jar", partsLine[1]); + assertEquals("%srv%/spi 1 got.jar", partsLine[2]); + } + + @Test(expected = UnknowCommandException.class) + public void testUnknowCommand() throws UnknowCommandException { + new BuildScript("ololo push pop"); + } + + @Test + public void testExecuteMkdir() throws CommandException, IOException { + String script = "mkdir %dir%/someDir"; + BuildScript buildScript = new BuildScript(script); + buildScript.setVariables(variables); + buildScript.execute(); + + File dir = new File(tempDirectory, "someDir"); + assertTrue(dir.exists()); + } + + @Test + public void testExecuteLnk() throws CommandException, IOException { + String script = "mkdir %dir%/someDir\n" + + "lnk %dir%/someDir %dir%/dirLink"; + BuildScript buildScript = new BuildScript(script); + buildScript.setVariables(variables); + buildScript.execute(); + + File dir = new File(tempDirectory, "someDir"); + assertTrue(dir.exists()); + assertTrue(dir.isDirectory()); + + File dirLink = new File(tempDirectory, "dirLink"); + assertTrue(dirLink.exists()); + assertTrue(dirLink.isDirectory()); + } + + private void prepareTestExecuteCopy() throws IOException { + File dir = new File(tempDirectory, "someDir"); + assertTrue(dir.mkdir()); + + FileWriter fw = new FileWriter(new File(dir, "file1")); + fw.write("123\n456"); + fw.close(); + fw = new FileWriter(new File(dir, "file2")); + fw.write("789\n101112"); + fw.close(); + assertTrue(new File(dir, "indir").mkdir()); + fw = new FileWriter(new File(dir, "indir/file3")); + fw.write("131415\n161718"); + fw.close(); + } + + @Test + public void testExecuteCopy() throws CommandException, IOException { + prepareTestExecuteCopy(); + + String script = "copy %dir%/someDir %dir%/newDir"; + BuildScript buildScript = new BuildScript(script); + buildScript.setVariables(variables); + buildScript.execute(); + + // проверка результатов + File file = new File(tempDirectory, "newDir/file1"); + assertTrue(file.exists()); + assertTrue(file.isFile()); + + file = new File(tempDirectory, "newDir/file2"); + assertTrue(file.exists()); + assertTrue(file.isFile()); + + file = new File(tempDirectory, "newDir/indir"); + assertTrue(file.exists()); + assertTrue(file.isDirectory()); + + file = new File(tempDirectory, "newDir/indir/file3"); + assertTrue(file.exists()); + assertTrue(file.isFile()); + } + + private void genListFile(List list, File node, String sourcePathRoot) { + if (node.isFile()) { + String filePath = node.getAbsolutePath(); + list.add(filePath.substring(sourcePathRoot.length()+1, filePath.length())); + } else if (node.isDirectory()) { + File[] files = node.listFiles(); + if (files != null) { + for (File file : files) { + genListFile(list, file, sourcePathRoot); + } + } + } + } + + private void prepareTestExeuteUnzip() throws IOException { + prepareTestExecuteCopy(); + + List listFiles = new ArrayList<>(); + genListFile(listFiles, new File(tempDirectory, "someDir"), tempDirectory.getAbsolutePath()); + + FileOutputStream fos = new FileOutputStream(new File(tempDirectory, "arch.zip")); + ZipOutputStream zos = new ZipOutputStream(fos); + byte[] buffer = new byte[16]; + + for (String fileStr : listFiles) { + ZipEntry zipEntry = new ZipEntry(fileStr); + zos.putNextEntry(zipEntry); + + FileInputStream fis = new FileInputStream(tempDirectory.getAbsolutePath() + File.separator + fileStr); + int len; + while ((len = fis.read(buffer)) > 0) { + zos.write(buffer, 0, len); + } + fis.close(); + } + + zos.closeEntry(); + zos.close(); + } + + @Test + public void testExeuteUnzip() throws CommandException, IOException { + prepareTestExeuteUnzip(); + + String script = "unpack %dir%/arch.zip %dir%/unpack"; + BuildScript buildScript = new BuildScript(script); + buildScript.setVariables(variables); + buildScript.execute(); + + // проверка результатов + File file = new File(tempDirectory, "unpack/someDir/file1"); + assertTrue(file.exists()); + assertTrue(file.isFile()); + + file = new File(tempDirectory, "unpack/someDir/file2"); + assertTrue(file.exists()); + assertTrue(file.isFile()); + + file = new File(tempDirectory, "unpack/someDir/indir"); + assertTrue(file.exists()); + assertTrue(file.isDirectory()); + + file = new File(tempDirectory, "unpack/someDir/indir/file3"); + assertTrue(file.exists()); + assertTrue(file.isFile()); + } +} diff --git a/pom.xml b/pom.xml index c7d9f86..bcb1118 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,7 @@ 0.2 UTF-8 1.8 + 1.7.20 asys @@ -25,13 +26,25 @@ org.slf4j slf4j-api - 1.7.20 + ${slf4j.version} org.osgi org.osgi.core 6.0.0 + + junit + junit + 4.12 + test + + + org.slf4j + slf4j-jdk14 + ${slf4j.version} + test +