X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=org.simantics.maps.server%2Fsrc%2Forg%2Fsimantics%2Fdistrict%2Fmaps%2Fserver%2FTileserverMapnik.java;h=ef17ee06829db70a30a5c2ad45c94ab631345e68;hb=refs%2Fchanges%2F06%2F3606%2F1;hp=8f3135dc567c2ddf18124bfaa7a70c14c77dbab1;hpb=2529be6d456deeb07c128603ce4971f1dc29b695;p=simantics%2Fdistrict.git diff --git a/org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java b/org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java index 8f3135dc..ef17ee06 100644 --- a/org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java +++ b/org.simantics.maps.server/src/org/simantics/district/maps/server/TileserverMapnik.java @@ -1,6 +1,5 @@ package org.simantics.district.maps.server; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -12,16 +11,19 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; +import org.simantics.district.maps.server.prefs.MapsServerPreferences; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; +import org.zeroturnaround.exec.InvalidExitValueException; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.StartedProcess; -import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; +import org.zeroturnaround.exec.stream.slf4j.Slf4jDebugOutputStream; import org.zeroturnaround.process.PidProcess; import org.zeroturnaround.process.PidUtil; import org.zeroturnaround.process.Processes; @@ -34,21 +36,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class TileserverMapnik { private static final Logger LOGGER = LoggerFactory.getLogger(TileserverMapnik.class); - private static final String[] ADDITIONAL_DEPENDENCIES = new String[] { "tilelive-vector@3.9.4", "tilelive-tmstyle@0.6.0" }; private SystemProcess process; private Path serverRoot; - private int port; private AtomicBoolean running = new AtomicBoolean(false); - TileserverMapnik(Path serverRoot, int port) { + TileserverMapnik(Path serverRoot) { this.serverRoot = serverRoot.normalize(); - this.port = port; - } - - public void setPort(int port) { - this.port = port; } public boolean isRunning() throws IOException, InterruptedException { @@ -59,19 +54,25 @@ public class TileserverMapnik { return running.get(); } - public void start() throws Exception { + public void start(Optional listener) throws Exception { // check if existing server is left hanging if (Files.exists(getPid())) { String pid = new String(Files.readAllBytes(getPid())); PidProcess pr = Processes.newPidProcess(Integer.parseInt(pid)); - pr.destroyForcefully(); + try { + pr.destroyForcefully(); + } catch (InvalidExitValueException e) { + // ignore, + } catch (Exception e) { + LOGGER.error("Could not destroy process with pid {}", pid, e); + } } // check that npm dependencies are satisfied - if (checkAndInstall(null)) { - checkAndInstall(ADDITIONAL_DEPENDENCIES[0]); - checkAndInstall(ADDITIONAL_DEPENDENCIES[1]); - } +// if (checkAndInstall(null, listener)) { +// checkAndInstall(ADDITIONAL_DEPENDENCIES[0], listener); +// checkAndInstall(ADDITIONAL_DEPENDENCIES[1], listener); +// } checkConfigJson(); checkTm2Styles(); @@ -79,15 +80,26 @@ public class TileserverMapnik { if (process != null && process.isAlive()) return; - StartedProcess startedProcess = new ProcessExecutor().directory(serverRoot.resolve("tileserver-mapnik").toFile()).destroyOnExit().environment(getEnv()) - .command(NodeJS.executable().toString(), getTessera().toString(), "-c", getConfigJson().toString()) - .redirectOutput(Slf4jStream.ofCaller().asDebug()).start(); + Path tileliveTesseraWin = serverRoot.resolve("dist").resolve("node.exe").normalize().toAbsolutePath(); + StartedProcess startedProcess = new ProcessExecutor().directory(serverRoot.resolve("dist").toFile()).destroyOnExit().environment(getEnv()) + .command(tileliveTesseraWin.toString(), "-c", getConfigJson().toString(), "-p", Integer.toString(MapsServerPreferences.defaultPort())) + .redirectOutput(new Slf4jDebugOutputStream(LOGGER) { + + @Override + protected void processLine(String line) { + // Convert to UTF-8 string + String utf8Line = new String(line.getBytes(), StandardCharsets.UTF_8); + log.debug(utf8Line); + } + }).start(); Process nativeProcess = startedProcess.getProcess(); process = Processes.newStandardProcess(nativeProcess); int pid = PidUtil.getPid(nativeProcess); + LOGGER.info("Writing pid-file to {} with pid={}", getPid(), pid); Files.write(getPid(), (pid + "").getBytes(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); running.set(true); + listener.ifPresent(TileserverStartListener::started); } private Map getEnv() { @@ -116,46 +128,57 @@ public class TileserverMapnik { return serverRoot.resolve("pid"); } - public void restart() throws Exception { + public void restart(Optional listener) throws Exception { stop(); - start(); - } - - private boolean checkIfInstalled(String module) throws Exception { - String tileserverMapnik = tileserverMapnikRoot().toString(); - int retVal; - try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { - if (module == null) { - retVal = NodeJS.npm(output, "--prefix", tileserverMapnik, "list"); - } else { - retVal = NodeJS.npm(output, "--prefix", tileserverMapnik, "list", module); - } - String outputString = new String(output.toByteArray(), StandardCharsets.UTF_8); - } - return retVal == 0; + start(listener); } - private boolean install(String module) throws Exception { - String tileserverMapnik = tileserverMapnikRoot().toString(); - int retVal; - if (module == null) - retVal = NodeJS.npm(null, "--prefix", tileserverMapnik, "install", "--save"); - else - retVal = NodeJS.npm(null, "--prefix", tileserverMapnik, "install", module, "--save"); - if (retVal != 0) - LOGGER.warn("Could not install module " + module == null ? "package.json" : module + "! " + retVal); - return retVal == 0; - } - - private boolean checkAndInstall(String module) throws Exception { - boolean installed = checkIfInstalled(module); - if (!installed) - install(module); - return !installed; - } +// private boolean checkIfInstalled(String module) throws Exception { +// String tileserverMapnik = tileserverMapnikRoot().toString(); +// int retVal; +// try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { +// if (module == null) { +// retVal = NodeJS.npm(output, "--prefix", tileserverMapnik, "list"); +// } else { +// retVal = NodeJS.npm(output, "--prefix", tileserverMapnik, "list", module); +// } +// String outputString = new String(output.toByteArray(), StandardCharsets.UTF_8); +// } +// return retVal == 0; +// } +// +// private int install(String module, Optional listener) throws Exception { +// String tileserverMapnik = tileserverMapnikRoot().toString(); +// int retVal; +// if (module == null) { +// listener.ifPresent(l -> l.installing("Installing tileserver-mapnik")); +// retVal = NodeJS.npm(null, "--prefix", tileserverMapnik, "install", "--save"); +// } else { +// retVal = NodeJS.npm(null, "--prefix", tileserverMapnik, "install", module, "--save"); +// } +// if (retVal != 0) +// LOGGER.warn("Could not install module " + module == null ? "package.json" : module + "! " + retVal); +// return retVal; +// } +// +// private boolean checkAndInstall(String module, Optional listener) throws Exception { +// LOGGER.info("Installing module {}", String.valueOf(module)); +// boolean installed = checkIfInstalled(module); +// if (!installed) { +// int installSuccessfull = install(module, listener); +// if (installSuccessfull != 0) { +// LOGGER.warn("Installation of module {} failed!", String.valueOf(module)); +// listener.ifPresent(l -> l.installationFailed(module, installSuccessfull)); +// } +// } else { +// LOGGER.info("Module {} was already installed", String.valueOf(module)); +// } +// return !installed; +// } - private Path tileserverMapnikRoot() { + @SuppressWarnings("unused") + private Path tileserverMapnikRoot() { return serverRoot.resolve("tileserver-mapnik").toAbsolutePath(); } @@ -164,10 +187,11 @@ public class TileserverMapnik { } private Path getICU() { - return serverRoot.resolve("tileserver-mapnik/node_modules/tilelive-vector/node_modules/mapnik/lib/binding/node-v46-win32-x64/share/icu").toAbsolutePath(); + return serverRoot.resolve("dist/share/icu").toAbsolutePath(); } - private Path getTessera() { + @SuppressWarnings("unused") + private Path getTessera() { return serverRoot.resolve("tileserver-mapnik/bin/tessera.js").toAbsolutePath(); } @@ -176,7 +200,7 @@ public class TileserverMapnik { } public List availableMBTiles() throws IOException { - Path data = serverRoot.resolve("data").toAbsolutePath(); + Path data = getDataDirectory(); List result = new ArrayList<>(); try (Stream paths = Files.walk(data)) { paths.forEach(p -> { @@ -192,7 +216,7 @@ public class TileserverMapnik { private void checkConfigJson() throws JsonParseException, JsonMappingException, IOException { Path configJson = getConfigJson(); Map config = new HashMap<>(); - Path tm2 = serverRoot.resolve("tm2").toAbsolutePath(); + Path tm2 = getStyleDirectory(); try (DirectoryStream stream = Files.newDirectoryStream(tm2)) { stream.forEach(p -> { Path projectYaml = p.resolve("project.yml"); @@ -214,8 +238,21 @@ public class TileserverMapnik { mapper.writerWithDefaultPrettyPrinter().writeValue(Files.newOutputStream(configJson, StandardOpenOption.TRUNCATE_EXISTING), config); } + public Path getStyleDirectory() { + return serverRoot.resolve("tm2"); + } + + public Path getDataDirectory() { + return serverRoot.resolve("data"); + } + + public Path getCurrentTiles() { + return getDataDirectory().resolve(MapsServerPreferences.currentMBTiles()); + } + + @SuppressWarnings("unchecked") public void checkTm2Styles() { - Path tm2 = serverRoot.resolve("tm2").toAbsolutePath(); + Path tm2 = getStyleDirectory(); try (DirectoryStream stream = Files.newDirectoryStream(tm2)) { stream.forEach(p -> { Path projectYaml = p.resolve("project.yml"); @@ -224,8 +261,7 @@ public class TileserverMapnik { try (InputStream input = Files.newInputStream(projectYaml, StandardOpenOption.READ)) { data = yaml.loadAs(input, Map.class); - Path tiles = serverRoot.relativize(serverRoot.resolve("data").resolve("helsinki_finland.mbtiles"));//.toAbsolutePath().toString().replace("\\", "/"); - + Path tiles = serverRoot.relativize(getCurrentTiles()); String tmStyle = "mbtiles://../" + tiles.toString(); data.put("source", tmStyle); @@ -246,7 +282,7 @@ public class TileserverMapnik { public List listStyles() throws IOException { List results = new ArrayList<>(); - Path tm2 = serverRoot.resolve("tm2").toAbsolutePath(); + Path tm2 = getStyleDirectory(); try (DirectoryStream stream = Files.newDirectoryStream(tm2)) { stream.forEach(p -> { results.add(p.getFileName().toString()); @@ -255,4 +291,12 @@ public class TileserverMapnik { return results; } + public static interface TileserverStartListener { + + void installing(String module); + + void installationFailed(String module, int returnValue); + + void started(); + } }