Skip to content

Commit

Permalink
Merge pull request #115 from linyimin0812/refactor/20231009_directory…
Browse files Browse the repository at this point in the history
…_watcher_1

refactor: use directory watcher
  • Loading branch information
linyimin0812 authored Oct 15, 2023
2 parents 75eef2a + caf1933 commit 617f770
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 160 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ jobs:
run: |
cd ./spring-startup-cli
mvn clean package
mvn -Pnative -Dagent exec:exec@java-agent
java -agentlib:native-image-agent=config-output-dir=./target/native/agent-output/main/ -jar ./target/spring-startup-cli-1.0-SNAPSHOT-jar-with-dependencies.jar exec:exec@java-agent
mvn -DskipTests=true -Pnative -Dagent package
cp ./target/spring-startup-cli${{ matrix.binaryExt }} ./target/spring-startup-cli-${{ matrix.name }}${{ matrix.binaryExt }}
Expand Down
60 changes: 10 additions & 50 deletions spring-startup-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mainClass>io.github.linyimin0812.spring.startup.cli.CliMain</mainClass>
<native.maven.plugin.version>0.9.27</native.maven.plugin.version>
<imageName>spring-startup-cli</imageName>
Expand Down Expand Up @@ -44,7 +44,14 @@
<version>2.0.40</version>
</dependency>

<dependency>
<!-- https://mvnrepository.com/artifact/io.methvin/directory-watcher -->
<dependency>
<groupId>io.methvin</groupId>
<artifactId>directory-watcher</artifactId>
<version>0.18.0</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
Expand All @@ -57,20 +64,6 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>info.picocli</groupId>
<artifactId>picocli-codegen</artifactId>
<version>4.7.5</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
</compilerArgs>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.source}</target>
</configuration>
</plugin>

<plugin>
Expand Down Expand Up @@ -166,39 +159,6 @@
<imageName>${imageName}</imageName>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>java-agent</id>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>java</executable>
<workingDirectory>${project.build.directory}</workingDirectory>
<arguments>
<argument>-classpath</argument>
<classpath/>
<argument>${mainClass}</argument>
<argument>exec:exec@java-agent</argument>
</arguments>
</configuration>
</execution>
<execution>
<id>native</id>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>${project.build.directory}/${imageName}</executable>
<workingDirectory>${project.build.directory}</workingDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
Expand All @@ -42,7 +43,7 @@ public static void main(String[] args) {

// only for run mvn -Pnative -Dagent exec:exec@java-agent
if (args.length > 0 && "exec:exec@java-agent".equals(args[0])) {
commands.close();
forNativeTracingAgent();
return;
}

Expand Down Expand Up @@ -115,4 +116,29 @@ public static String prompt() {

return Constants.CLI_NAME + " (" + currentBranch + ") > ";
}

private static void forNativeTracingAgent() {

Path file = Paths.get(System.getProperty(Constants.USER_DIR), Constants.SOURCE_DIR, "forNativeTracingAgent.java");

new Thread(() -> {
try {

Files.deleteIfExists(file);

Files.createFile(file);

Thread.sleep(1000);

} catch (IOException | InterruptedException ignored) {
} finally {
try {
commands.close();
Files.deleteIfExists(file);

} catch (IOException ignored) {
}
}
}).start();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.github.linyimin0812.spring.startup.jdwp.command.RedefineClassesCommand;
import io.github.linyimin0812.spring.startup.jdwp.command.RedefineClassesReplyPackage;
import io.github.linyimin0812.spring.startup.utils.ModuleUtil;
import io.methvin.watcher.DirectoryChangeEvent;

import java.io.File;
import java.io.IOException;
Expand All @@ -20,30 +21,29 @@
import java.util.concurrent.ConcurrentHashMap;

import static io.github.linyimin0812.spring.startup.constant.Constants.OUT;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;

/**
* @author linyimin
**/
public class ModifiedFileProcessor {

private final Map<String, WatchEvent.Kind<Path>> FILE_WATCH_EVENTS = new ConcurrentHashMap<>();
private final Map<String, DirectoryChangeEvent> FILE_WATCH_EVENTS = new ConcurrentHashMap<>();
private final Map<String /* class qualifier */, Path> RECOMIPLED_FILE_MAP = new ConcurrentHashMap<>();

public ModifiedFileProcessor() {
// TODO: 初始化时从git获取所有修改文件
}

public void onEvent(Path dir, WatchEvent.Kind<Path> eventKind) {
public void onEvent(DirectoryChangeEvent event) {

String path = dir.toString();
String path = event.path().toString();

if (!FILE_WATCH_EVENTS.containsKey(path)) {
OUT.printf("\n[INFO] - [%s] %s\n", eventKind.name().replace("ENTRY_", Constants.EMPTY_STRING), path);
OUT.printf("\n[INFO] - [%s] %s\n", event.eventType().name(), path);
OUT.printf(CliMain.prompt());
}

FILE_WATCH_EVENTS.put(path, eventKind);
FILE_WATCH_EVENTS.put(path, event);
}

/**
Expand Down Expand Up @@ -76,7 +76,7 @@ public boolean check() {
return false;
}

boolean anyAdded = FILE_WATCH_EVENTS.values().stream().anyMatch(kind -> kind == ENTRY_CREATE);
boolean anyAdded = FILE_WATCH_EVENTS.values().stream().anyMatch(event -> event.eventType() == DirectoryChangeEvent.EventType.CREATE);

if (anyAdded) {
OUT.println("Hotswap does not support adding new files, please restart the application");
Expand Down Expand Up @@ -176,7 +176,7 @@ private RedefineClassesCommand buildRedefineClassesCommand(Map<String, Long> loa

String fileNameWithoutPrefix = changeFile.getFileName().toString().replace(Constants.SOURCE_PREFIX, Constants.EMPTY_STRING);

Files.walkFileTree(Paths.get(compilePath), new SimpleFileVisitor<>() {
Files.walkFileTree(Paths.get(compilePath), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,52 +3,21 @@
import io.github.linyimin0812.spring.startup.constant.Constants;
import io.github.linyimin0812.spring.startup.utils.ModuleUtil;
import io.github.linyimin0812.spring.startup.utils.StringUtil;
import io.methvin.watcher.DirectoryWatcher;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static io.github.linyimin0812.spring.startup.constant.Constants.OUT;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardWatchEventKinds.*;
import java.util.stream.Collectors;

/**
* @author linyimin
**/
public class ModifiedFileWatcher {

private final WatchService watcher;
private final Map<WatchKey, Path> keys;

private final ModifiedFileProcessor processor;

private boolean running = true;

/**
* Register the given directory, and all its subdirectories, with the
* WatchService.
*/
private void registerAll(final Path start) throws IOException {

if (!start.toFile().exists()) {
return;
}

// register directory and subdirectories
Files.walkFileTree(start, new SimpleFileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
private final DirectoryWatcher watcher;

WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
keys.put(key, dir);

return FileVisitResult.CONTINUE;
}
});
}
public boolean running = false;

public ModifiedFileWatcher(ModifiedFileProcessor processor) throws IOException {
this(System.getProperty(Constants.USER_DIR), processor);
Expand All @@ -61,81 +30,36 @@ public ModifiedFileWatcher(String dir, ModifiedFileProcessor processor) throws I

Path path = Paths.get(dir);

this.keys = new HashMap<>();
this.processor = processor;
this.watcher = FileSystems.getDefault().newWatchService();

List<Path> moduleHomes = ModuleUtil.getModulePaths(path);

List<Path> moduleSourceDirs = moduleHomes.stream().map(moduleHome -> moduleHome.resolve(Constants.SOURCE_DIR)).filter(Files::exists).collect(Collectors.toList());

this.watcher = DirectoryWatcher.builder()
.paths(moduleSourceDirs)
.listener(processor::onEvent)
.build();

int longest = moduleHomes.stream().map(Path::toString).map(String::length).max(Integer::compareTo).orElse(0) + 32;

for (Path moduleHome : moduleHomes) {
OUT.format("[INFO] %s WATCHING\n", StringUtil.rightPad(moduleHome.toString() + Constants.SPACE, longest, "."));
registerAll(moduleHome.resolve(Constants.SOURCE_DIR));
System.out.format("[INFO] %s WATCHING\n", StringUtil.rightPad(moduleHome.toString() + Constants.SPACE, longest, "."));
}

new Thread(this::processEvents).start();
}

/**
* Process all events for keys queued to the watcher
*/
private void processEvents() {

while (running) {

// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException | ClosedWatchServiceException ignored) {
OUT.println("[INFO] File-Watcher closed");
return;
}

Path dir = keys.get(key);
if (dir == null) {
System.err.println("WatchKey not recognized!!");
continue;
}

for (WatchEvent<?> event: key.pollEvents()) {

WatchEvent.Kind<?> kind = event.kind();

// TBD - provide example of how OVERFLOW event is handled
if (kind == OVERFLOW) {
continue;
}

// Context for directory entry event is the file name of entry
@SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path child = dir.resolve(ev.context());

if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
continue;
}
new Thread(watcher::watch).start();

this.processor.onEvent(child, ev.kind());

}

// reset key and remove from set if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
keys.remove(key);

// all directories are inaccessible
if (keys.isEmpty()) {
break;
}
}
}
running = true;
}

public void close() throws IOException {
this.running = false;
running = false;
this.watcher.close();
}

public static void main(String[] args) throws IOException, InterruptedException {
ModifiedFileWatcher recompileFileWatcher = new ModifiedFileWatcher("/Users/banzhe/IdeaProjects/project/spring-boot-async-bean-demo/", new ModifiedFileProcessor());

Thread.sleep(20 * 1000);

recompileFileWatcher.close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static Result execute(String[] cmdArray, boolean print) {
}
}

String content = !sb.isEmpty() ? sb.substring(0, sb.length() - 1) : Constants.EMPTY_STRING;
String content = sb.length() > 0 ? sb.substring(0, sb.length() - 1) : Constants.EMPTY_STRING;

return new Result(code, content);
}
Expand Down

0 comments on commit 617f770

Please sign in to comment.