diff --git a/pom.xml b/pom.xml
index b244a42..bd85f3a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
net.slavus.logcock
logcock-app
- 0.0.1-SNAPSHOT
+ 0.0.1
jar
logcock-app
@@ -15,7 +15,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.3.0.RELEASE
+ 2.7.2
@@ -71,12 +71,13 @@
org.apache.commons
commons-lang3
-
+
org.webjars
@@ -194,13 +195,7 @@
org.springframework.boot
spring-boot-maven-plugin
-
-
- org.springframework.boot.experimental
- spring-boot-thin-layout
- 1.0.24.RELEASE
-
-
+
diff --git a/src/main/java/net/slavus/logcock/files/FilesController.java b/src/main/java/net/slavus/logcock/files/FilesController.java
index 9f12c95..0d62578 100644
--- a/src/main/java/net/slavus/logcock/files/FilesController.java
+++ b/src/main/java/net/slavus/logcock/files/FilesController.java
@@ -1,107 +1,134 @@
-package net.slavus.logcock.files;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.apache.commons.lang3.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.FileSystemResource;
-import org.springframework.core.io.Resource;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Controller;
-import org.springframework.ui.Model;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.ResponseBody;
-
-import net.slavus.logcock.LogcockProperties;
-import reactor.core.publisher.Mono;
-
-/**
- * @author slavus
- *
- */
-@Controller
-public class FilesController {
-
- @Autowired
- private LogcockProperties properties;
-
- @GetMapping("/")
- public String index() {
- return "redirect:/b/";
- }
-
- @GetMapping("/b")
- public String browseFolder(Model model) {
- model.addAttribute("basePath", "/");
- model.addAttribute("baseId", "");
- model.addAttribute("filesInDir", properties.getFolders());
- model.addAttribute("breadcrumbs", new ArrayList<>());
- return "index";
- }
-
- @GetMapping("/b/{id}/{*webPath}")
- public String browseFolder(@PathVariable Integer id, @PathVariable String webPath, Model model) {
- String basePath = properties.getFolders().get(id).getBasePath();
-
- String path = basePath + File.separator + webPath;
- model.addAttribute("basePath", basePath);
- model.addAttribute("baseId", id);
- model.addAttribute("filesInDir", fileList(path));
- model.addAttribute("breadcrumbs", breadcrumbs(path, basePath));
- return "index";
- }
-
- @GetMapping("/d/{id}/{*webPath}")
- @ResponseBody
- private Mono> downloadFile(@PathVariable Integer id, @PathVariable String webPath, Model model) {
- String basePath = properties.getFolders().get(id).getBasePath();
- String filePath = basePath + File.separator + webPath;
- FileSystemResource fileSystemResource = new FileSystemResource(filePath);
- return Mono.just(ResponseEntity
- .ok()
- .header("Content-Type", "application/octet-stream")
- .header("Content-Disposition", "attachment; filename=" + fileSystemResource.getFilename())
- .body(fileSystemResource)
- );
- }
-
-
- private List breadcrumbs(String path, String basePath) {
- final List crumbs = new ArrayList<>();
- File f = new File(path);
- while(StringUtils.startsWith(f.getAbsolutePath(), basePath)) {
- crumbs.add(f);
- f = f.getParentFile();
- }
- Collections.reverse(crumbs);
- return crumbs;
- }
-
-
- public static List fileList(String path) {
- File dir = new File(path);
- if(!dir.exists()) {
- return new ArrayList<>();
- }
- List files = Arrays.stream(dir.listFiles()).sorted((o1, o2) -> {
- boolean o1dir = o1.isDirectory();
- boolean o2dir = o2.isDirectory();
-
- if (o1dir == o2dir)
- return 0;
- if (o1dir)
- return -1;
- if (o2dir)
- return 1;
- return 0;
- }).collect(Collectors.toList());
- return dir.exists() ? files : new ArrayList<>();
- }
-
-}
+package net.slavus.logcock.files;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import net.slavus.logcock.LogcockProperties;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author slavus
+ *
+ */
+@Controller
+public class FilesController {
+
+ @Autowired
+ private LogcockProperties properties;
+
+ @Autowired
+ private ServerProperties serverProperties;
+
+ @Autowired
+ Files files;
+
+ @GetMapping("/")
+ public String index() {
+ return "redirect:/b/";
+ }
+
+ @GetMapping("/b")
+ public String browseFolder(Model model) {
+ model.addAttribute("basePath", "/");
+ model.addAttribute("baseId", "");
+ model.addAttribute("filesInDir", properties.getFolders());
+ model.addAttribute("breadcrumbs", new ArrayList<>());
+ return "index";
+ }
+
+ @GetMapping("/b/{id}/{*webPath}")
+ public String browseFolder(@PathVariable Integer id, @PathVariable String webPath, Model model) {
+ String basePath = properties.getFolders().get(id).getBasePath();
+
+ String path = basePath + File.separator + webPath;
+ model.addAttribute("basePath", basePath);
+ model.addAttribute("baseId", id);
+ model.addAttribute("filesInDir", fileList(path));
+ model.addAttribute("breadcrumbs", breadcrumbs(path, basePath));
+ return "index";
+ }
+
+
+ @ResponseBody
+ @GetMapping(path = "/b/{id}/{*webPath}", consumes = MediaType.APPLICATION_JSON_VALUE)
+ public List jsonBrowseFolder(@PathVariable Integer id, @PathVariable String webPath, Model model, ServerHttpRequest req) {
+ String basePath = properties.getFolders().get(id).getBasePath();
+
+ String scheme = req.getURI().getScheme();
+ String host = req.getHeaders().getHost().toString(); // includes server name and server port
+
+ String serverPath = scheme + "://" + host + StringUtils.defaultString(serverProperties.getServlet().getContextPath());
+
+ String path = basePath + File.separator + webPath;
+ return fileList(path)
+ .stream().map(f->serverPath + "/d/" + id + files.linkPath(f, basePath))
+ .collect(Collectors.toList());
+ }
+
+
+ @GetMapping("/d/{id}/{*webPath}")
+ @ResponseBody
+ private Mono> downloadFile(@PathVariable Integer id, @PathVariable String webPath, Model model) {
+ String basePath = properties.getFolders().get(id).getBasePath();
+ String filePath = basePath + File.separator + webPath;
+ FileSystemResource fileSystemResource = new FileSystemResource(filePath);
+ return Mono.just(ResponseEntity
+ .ok()
+ .header("Content-Type", "application/octet-stream")
+ .header("Content-Disposition", "attachment; filename=" + fileSystemResource.getFilename())
+ .body(fileSystemResource)
+ );
+ }
+
+
+ private List breadcrumbs(String path, String basePath) {
+ final List crumbs = new ArrayList<>();
+ File f = new File(path);
+ while(StringUtils.startsWith(f.getAbsolutePath(), basePath)) {
+ crumbs.add(f);
+ f = f.getParentFile();
+ }
+ Collections.reverse(crumbs);
+ return crumbs;
+ }
+
+
+ public static List fileList(String path) {
+ File dir = new File(path);
+ if(!dir.exists()) {
+ return new ArrayList<>();
+ }
+ List files = Arrays.stream(dir.listFiles()).sorted((o1, o2) -> {
+ boolean o1dir = o1.isDirectory();
+ boolean o2dir = o2.isDirectory();
+
+ if (o1dir == o2dir)
+ return 0;
+ if (o1dir)
+ return -1;
+ if (o2dir)
+ return 1;
+ return 0;
+ }).collect(Collectors.toList());
+ return dir.exists() ? files : new ArrayList<>();
+ }
+
+}
diff --git a/src/main/java/net/slavus/logcock/tail/FileTailer.java b/src/main/java/net/slavus/logcock/tail/FileTailer.java
index 9d4bfb7..9dddb30 100644
--- a/src/main/java/net/slavus/logcock/tail/FileTailer.java
+++ b/src/main/java/net/slavus/logcock/tail/FileTailer.java
@@ -1,122 +1,121 @@
-package net.slavus.logcock.tail;
-
-import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.file.FileSystems;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.WatchEvent;
-import java.nio.file.WatchKey;
-import java.nio.file.WatchService;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.stream.Collectors;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.Lists;
-
-import reactor.core.Disposable;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.FluxSink;
-
-public class FileTailer{
- private static final Logger LOGGER = LoggerFactory.getLogger(FileTailer.class);
-
- public static void main(String[] args) throws IOException, InterruptedException {
- FileTailer ft = new FileTailer(Executors.newFixedThreadPool(1), "logcock.log");
- Flux flux = ft.fileTailFlux();
- flux.subscribe(System.out::println);
- }
-
- private long lastKnownPosition = 0;
- private volatile boolean cancled = false;
- File tailedFile;
- private ExecutorService executorService;
-
- public FileTailer(ExecutorService executorService, String filename) {
- this.executorService = executorService;
- tailedFile = new File(filename);
- }
-
- public Flux fileTailFlux() {
- return Flux.create(sink -> {
- Disposable onExit= () -> cancled = true;
-
- sink.onCancel(onExit);
- sink.onDispose(onExit);
-
- //read initial lines
- try {
- List readLastLines = readLastLines();
- if (!readLastLines.isEmpty()) {
- String lines = readLastLines.stream().collect(Collectors.joining("\n"));
- LOGGER.debug("New lines: {}", lines);
- sink.next(lines);
- }
-
- } catch (IOException e) {
- sink.error(e);
- }
-
- executorService.execute(() -> {
- try {
- watchFile(sink);
- } catch (InterruptedException | IOException e) {
- sink.error(e);
- }
- });
- });
- }
-
- private void watchFile(FluxSink sink) throws IOException, InterruptedException {
- WatchService watchService = FileSystems.getDefault().newWatchService();
- WatchKey watchedKey = tailedFile.getAbsoluteFile().getParentFile().toPath().register(watchService, ENTRY_MODIFY);
-
- WatchKey key;
- while ((key = watchService.take()) != null) {
- for (WatchEvent> event : key.pollEvents()) {
- if (Files.isSameFile(tailedFile.getAbsoluteFile().toPath(), (Path) event.context())) {
- List readLastLines = readLastLines();
- if (!readLastLines.isEmpty()) {
- String lines = readLastLines.stream().collect(Collectors.joining("\n"));
- LOGGER.debug("New lines: {}", lines);
- sink.next(lines);
- }
- }
- }
- key.reset();
- if (cancled) {
- break;
- }
- }
- watchedKey.cancel();
- }
-
- private List readLastLines() throws IOException {
- List lines = Lists.newArrayList();
-
- long fileLength = tailedFile.length();
- if (fileLength > lastKnownPosition) {
-
- // Reading and writing file
- RandomAccessFile readWriteFileAccess;
- readWriteFileAccess = new RandomAccessFile(tailedFile, "rw");
- readWriteFileAccess.seek(lastKnownPosition);
- String crunentLine = null;
- while ((crunentLine = readWriteFileAccess.readLine()) != null) {
- lines.add(crunentLine);
- }
- lastKnownPosition = readWriteFileAccess.getFilePointer();
- readWriteFileAccess.close();
-
- }
- return lines;
- }
-
-}
+package net.slavus.logcock.tail;
+
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import reactor.core.Disposable;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.FluxSink;
+
+public class FileTailer{
+ private static final Logger LOGGER = LoggerFactory.getLogger(FileTailer.class);
+
+ public static void main(String[] args) throws IOException, InterruptedException {
+ FileTailer ft = new FileTailer(Executors.newFixedThreadPool(1), "logcock.log");
+ Flux flux = ft.fileTailFlux();
+ flux.subscribe(System.out::println);
+ }
+
+ private long lastKnownPosition = 0;
+ private volatile boolean cancled = false;
+ File tailedFile;
+ private ExecutorService executorService;
+
+ public FileTailer(ExecutorService executorService, String filename) {
+ this.executorService = executorService;
+ tailedFile = new File(filename);
+ }
+
+ public Flux fileTailFlux() {
+ return Flux.create(sink -> {
+ Disposable onExit= () -> cancled = true;
+
+ sink.onCancel(onExit);
+ sink.onDispose(onExit);
+
+ //read initial lines
+ try {
+ List readLastLines = readLastLines();
+ if (!readLastLines.isEmpty()) {
+ String lines = readLastLines.stream().collect(Collectors.joining("\n"));
+ LOGGER.debug("New lines: {}", lines);
+ sink.next(lines);
+ }
+
+ } catch (IOException e) {
+ sink.error(e);
+ }
+
+ executorService.execute(() -> {
+ try {
+ watchFile(sink);
+ } catch (InterruptedException | IOException e) {
+ sink.error(e);
+ }
+ });
+ });
+ }
+
+ private void watchFile(FluxSink sink) throws IOException, InterruptedException {
+ WatchService watchService = FileSystems.getDefault().newWatchService();
+ WatchKey watchedKey = tailedFile.getAbsoluteFile().getParentFile().toPath().register(watchService, ENTRY_MODIFY);
+
+ WatchKey key;
+ while ((key = watchService.take()) != null) {
+ for (WatchEvent> event : key.pollEvents()) {
+ if (Files.isSameFile(tailedFile.getAbsoluteFile().toPath(), (Path) event.context())) {
+ List readLastLines = readLastLines();
+ if (!readLastLines.isEmpty()) {
+ String lines = readLastLines.stream().collect(Collectors.joining("\n"));
+ LOGGER.debug("New lines: {}", lines);
+ sink.next(lines);
+ }
+ }
+ }
+ key.reset();
+ if (cancled) {
+ break;
+ }
+ }
+ watchedKey.cancel();
+ }
+
+ private List readLastLines() throws IOException {
+ List lines = new ArrayList<>();
+
+ long fileLength = tailedFile.length();
+ if (fileLength > lastKnownPosition) {
+
+ // Reading and writing file
+ RandomAccessFile readWriteFileAccess;
+ readWriteFileAccess = new RandomAccessFile(tailedFile, "rw");
+ readWriteFileAccess.seek(lastKnownPosition);
+ String crunentLine = null;
+ while ((crunentLine = readWriteFileAccess.readLine()) != null) {
+ lines.add(crunentLine);
+ }
+ lastKnownPosition = readWriteFileAccess.getFilePointer();
+ readWriteFileAccess.close();
+
+ }
+ return lines;
+ }
+
+}
diff --git a/src/test/java/net/slavus/logcock/LogcockAppApplicationTests.java b/src/test/java/net/slavus/logcock/LogcockAppApplicationTests.java
index f2e3859..d303b17 100644
--- a/src/test/java/net/slavus/logcock/LogcockAppApplicationTests.java
+++ b/src/test/java/net/slavus/logcock/LogcockAppApplicationTests.java
@@ -1,11 +1,8 @@
package net.slavus.logcock;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
-@RunWith(SpringRunner.class)
@SpringBootTest
public class LogcockAppApplicationTests {