के लिए क्या इसके लायक है, मैं अवधारणा का एक छोटा सबूत जो
- करने में सक्षम भी पता लगा कहा, संशोधित और एक देखा निर्देशिका में फ़ाइलों को नष्ट कर दिया हैक कर लिया है,
- एकीकृत प्रदर्शित प्रत्येक परिवर्तन के लिए भिन्नता (फ़ाइलों को जोड़ा/हटाए जाने पर भी पूर्ण diffs),
- स्रोत निर्देशिका की छाया प्रतिलिपि रखते हुए लगातार परिवर्तनों का ट्रैक रखते हुए,
- उपयोगकर्ता द्वारा परिभाषित लय में काम करता है (डिफ़ॉल्ट 5 सेकंड होता है) ताकि थोड़े समय में बहुत से छोटे अंतरों को मुद्रित न किया जा सके, बल्कि थोड़ी देर में कुछ हद तक बड़ा हो।
कई सीमाएं जो उत्पादन वातावरण में बाधाओं होगा रहे हैं:
- आदेश में आवश्यकता से अधिक नमूना कोड को मुश्किल नहीं करने के लिए, उप निर्देशिकाओं शुरुआत जब छाया निर्देशिका बनाई गई है पर कॉपी कर रहे हैं (क्योंकि मैंने एक गहरी निर्देशिका प्रतिलिपि बनाने के लिए एक मौजूदा विधि का पुनर्नवीनीकरण किया है), लेकिन रनटाइम के दौरान अनदेखा किया गया। रिकर्सन से बचने के लिए देखे गए निर्देशिका के ठीक नीचे केवल फाइलों की निगरानी की जा रही है।
- बाहरी पुस्तकालयों का उपयोग न करने की आपकी आवश्यकता पूरी नहीं हुई है क्योंकि मैं वास्तव में एकीकृत diff निर्माण के लिए पहिया को फिर से आविष्कार करना चाहता हूं।
- इस समाधान का सबसे बड़ा लाभ - यह टेक्स्ट फ़ाइल में कहीं भी परिवर्तनों का पता लगाने में सक्षम है, न केवल
tail -f
जैसी फ़ाइल के अंत में - यह भी सबसे बड़ा नुकसान है: जब भी कोई फ़ाइल बदलती है तो यह पूरी तरह से छाया-प्रतिलिपि होनी चाहिए क्योंकि अन्यथा कार्यक्रम बाद में परिवर्तन का पता नहीं लगा सकता है। तो मैं बहुत बड़ी फाइलों के लिए इस समाधान की सिफारिश नहीं करता।
निर्माण करने के लिए कैसे:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.tools</groupId>
<artifactId>SO_WatchServiceChangeLocationInFile</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.googlecode.java-diff-utils</groupId>
<artifactId>diffutils</artifactId>
<version>1.3.0</version>
</dependency>
</dependencies>
</project>
स्रोत कोड (क्षमा करें, थोड़ा लंबा):
package de.scrum_master.app;
import difflib.DiffUtils;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.LinkedList;
import java.util.List;
import static java.nio.file.StandardWatchEventKinds.*;
public class FileChangeWatcher {
public static final String DEFAULT_WATCH_DIR = "watch-dir";
public static final String DEFAULT_SHADOW_DIR = "shadow-dir";
public static final int DEFAULT_WATCH_INTERVAL = 5;
private Path watchDir;
private Path shadowDir;
private int watchInterval;
private WatchService watchService;
public FileChangeWatcher(Path watchDir, Path shadowDir, int watchInterval) throws IOException {
this.watchDir = watchDir;
this.shadowDir = shadowDir;
this.watchInterval = watchInterval;
watchService = FileSystems.getDefault().newWatchService();
}
public void run() throws InterruptedException, IOException {
prepareShadowDir();
watchDir.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
while (true) {
WatchKey watchKey = watchService.take();
for (WatchEvent<?> event : watchKey.pollEvents()) {
Path oldFile = shadowDir.resolve((Path) event.context());
Path newFile = watchDir.resolve((Path) event.context());
List<String> oldContent;
List<String> newContent;
WatchEvent.Kind<?> eventType = event.kind();
if (!(Files.isDirectory(newFile) || Files.isDirectory(oldFile))) {
if (eventType == ENTRY_CREATE) {
if (!Files.isDirectory(newFile))
Files.createFile(oldFile);
} else if (eventType == ENTRY_MODIFY) {
Thread.sleep(200);
oldContent = fileToLines(oldFile);
newContent = fileToLines(newFile);
printUnifiedDiff(newFile, oldFile, oldContent, newContent);
try {
Files.copy(newFile, oldFile, StandardCopyOption.REPLACE_EXISTING);
} catch (Exception e) {
e.printStackTrace();
}
} else if (eventType == ENTRY_DELETE) {
try {
oldContent = fileToLines(oldFile);
newContent = new LinkedList<>();
printUnifiedDiff(newFile, oldFile, oldContent, newContent);
Files.deleteIfExists(oldFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
watchKey.reset();
Thread.sleep(1000 * watchInterval);
}
}
private void prepareShadowDir() throws IOException {
recursiveDeleteDir(shadowDir);
Runtime.getRuntime().addShutdownHook(
new Thread() {
@Override
public void run() {
try {
System.out.println("Cleaning up shadow directory " + shadowDir);
recursiveDeleteDir(shadowDir);
} catch (IOException e) {
e.printStackTrace();
}
}
}
);
recursiveCopyDir(watchDir, shadowDir);
}
public static void recursiveDeleteDir(Path directory) throws IOException {
if (!directory.toFile().exists())
return;
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}
public static void recursiveCopyDir(final Path sourceDir, final Path targetDir) throws IOException {
Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.copy(file, Paths.get(file.toString().replace(sourceDir.toString(), targetDir.toString())));
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Files.createDirectories(Paths.get(dir.toString().replace(sourceDir.toString(), targetDir.toString())));
return FileVisitResult.CONTINUE;
}
});
}
private static List<String> fileToLines(Path path) throws IOException {
List<String> lines = new LinkedList<>();
String line;
try (BufferedReader reader = new BufferedReader(new FileReader(path.toFile()))) {
while ((line = reader.readLine()) != null)
lines.add(line);
}
catch (Exception e) {}
return lines;
}
private static void printUnifiedDiff(Path oldPath, Path newPath, List<String> oldContent, List<String> newContent) {
List<String> diffLines = DiffUtils.generateUnifiedDiff(
newPath.toString(),
oldPath.toString(),
oldContent,
DiffUtils.diff(oldContent, newContent),
3
);
System.out.println();
for (String diffLine : diffLines)
System.out.println(diffLine);
}
public static void main(String[] args) throws IOException, InterruptedException {
String watchDirName = args.length > 0 ? args[0] : DEFAULT_WATCH_DIR;
String shadowDirName = args.length > 1 ? args[1] : DEFAULT_SHADOW_DIR;
int watchInterval = args.length > 2 ? Integer.getInteger(args[2]) : DEFAULT_WATCH_INTERVAL;
new FileChangeWatcher(Paths.get(watchDirName), Paths.get(shadowDirName), watchInterval).run();
}
}
मैं डिफ़ॉल्ट सेटिंग्स (जैसे घड़ी का उपयोग एक स्रोत नामक निर्देशिका का उपयोग करने के लिए "की सिफारिश -dir ") और थोड़ी देर के लिए इसके साथ खेलें, कंसोल आउटपुट को देखते हुए जब आप एक संपादक में कुछ टेक्स्ट फाइलें बनाते हैं और संपादित करते हैं। यह सॉफ्टवेयर के आंतरिक यांत्रिकी को समझने में मदद करता है। अगर कुछ गलत हो जाता है, उदा।एक 5 सेकंड लय के भीतर एक फ़ाइल बनाई जाती है लेकिन फिर से जल्दी से हटा दी जाती है, कॉपी या डिफाई करने के लिए कुछ भी नहीं है, इसलिए प्रोग्राम सिर्फ System.err
पर एक स्टैक ट्रेस प्रिंट करेगा।
ऐसी चीज़ 'वॉच सेवा' के साथ संभव नहीं है। –
धन्यवाद। जावा 7/एनआईओ के भीतर कुछ भी है जो ऐसा करने में सक्षम हो सकता है? – Tony
ऐसा नहीं है कि मुझे पता है। आपको पहले/बाद में कक्षा के अपने स्कैन को लागू करने की आवश्यकता होगी। इस आईएमओ के लिए 'वॉच सर्विस' आदर्श नहीं होगा। –