package launchserver.binary;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarOutputStream;
import java.util.jar.Pack200;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import launcher.Launcher;
import launcher.Launcher.Config;
import launcher.LauncherAPI;
import launcher.helper.IOHelper;
import launcher.helper.LogHelper;
import launcher.helper.SecurityHelper;
import launcher.helper.SecurityHelper.DigestAlgorithm;
import launcher.serialize.HOutput;
import launchserver.LaunchServer;
public final class JARLauncherBinary extends LauncherBinary {
@LauncherAPI public final Path runtimeDir;
@LauncherAPI public final Path initScriptFile;
@LauncherAPI
public JARLauncherBinary(LaunchServer server) throws IOException {
super(server, server.dir.resolve("Launcher.jar"));
runtimeDir = server.dir.resolve(Launcher.RUNTIME_DIR);
initScriptFile = runtimeDir.resolve(Launcher.INIT_SCRIPT_FILE);
tryUnpackRuntime();
}
@Override
public void build() throws IOException {
tryUnpackRuntime();
// Build launcher binary
LogHelper.info("Building launcher binary file");
try (JarOutputStream output = new JarOutputStream(IOHelper.newOutput(binaryFile))) {
output.setMethod(ZipOutputStream.DEFLATED);
output.setLevel(Deflater.BEST_COMPRESSION);
try (InputStream input = new GZIPInputStream(IOHelper.newInput(IOHelper.getResourceURL("Launcher.pack.gz")))) {
Pack200.newUnpacker().unpack(input, output);
}
// Verify has init script file
if (!IOHelper.isFile(initScriptFile)) {
throw new IOException(String.format("Missing init script file ('%s')", Launcher.INIT_SCRIPT_FILE));
}
// Write launcher runtime dir
Map<String, byte[]> runtime = new HashMap<>(256);
IOHelper.walk(runtimeDir, new RuntimeDirVisitor(output, runtime), false);
// Create launcher config file
byte[] launcherConfigBytes;
try (ByteArrayOutputStream configArray = IOHelper.newByteArrayOutput()) {
try (HOutput configOutput = new HOutput(configArray)) {
new Config(server.config.getAddress(), server.config.port, server.publicKey, runtime).write(configOutput);
}
launcherConfigBytes = configArray.toByteArray();
}
// Write launcher config file
output.putNextEntry(IOHelper.newZipEntry(Launcher.CONFIG_FILE));
output.write(launcherConfigBytes);
}
}
@LauncherAPI
public void tryUnpackRuntime() throws IOException {
// Verify is runtime dir unpacked
if (IOHelper.isDir(runtimeDir)) {
return; // Already unpacked
}
// Unpack launcher runtime files
Files.createDirectory(runtimeDir);
LogHelper.info("Unpacking launcher runtime files");
try (ZipInputStream input = IOHelper.newZipInput(IOHelper.getResourceURL("launchserver/defaults/runtime.zip"))) {
for (ZipEntry entry = input.getNextEntry(); entry != null; entry = input.getNextEntry()) {
if (entry.isDirectory()) {
continue; // Skip dirs
}
// Unpack runtime file
IOHelper.transfer(input, runtimeDir.resolve(IOHelper.toPath(entry.getName())));
}
}
}
private static ZipEntry newEntry(String fileName) {
return IOHelper.newZipEntry(Launcher.RUNTIME_DIR + IOHelper.CROSS_SEPARATOR + fileName);
}
private final class RuntimeDirVisitor extends SimpleFileVisitor<Path> {
private final ZipOutputStream output;
private final Map<String, byte[]> runtime;
private RuntimeDirVisitor(ZipOutputStream output, Map<String, byte[]> runtime) {
this.output = output;
this.runtime = runtime;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
String dirName = IOHelper.toString(runtimeDir.relativize(dir));
output.putNextEntry(newEntry(dirName + '/'));
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String fileName = IOHelper.toString(runtimeDir.relativize(file));
runtime.put(fileName, SecurityHelper.digest(DigestAlgorithm.MD5, file));
// Create zip entry and transfer contents
output.putNextEntry(newEntry(fileName));
IOHelper.transfer(file, output);
// Return result
return super.visitFile(file, attrs);
}
}
}