Utilities for File Handling
Java module with utilities for handling files, paths and directories.
See morimekta.net/utils for procedures on releases.
Getting Started
To add to maven
: Add this line to pom.xml
under dependencies:
<dependency>
<groupId>net.morimekta.utils</groupId>
<artifactId>file</artifactId>
<version>4.1.2</version>
</dependency>
To add to gradle
: Add this line to the dependencies
group in build.gradle
:
implementation 'net.morimekta.utils:file:4.1.2'
File Watching
Watch updates on files and directories. This is updated for handling file
structures like that of modifiable kubernetes
configmap
, meaning it will
detect updates on intermediary symlinks.
Consists of two utilities and a listener interface.
DirWatcher
is a simple wrapper around the native java watcher creating a simple watcher class using listeners. It listens to direcories and gives events of file updates there. Each listener is registered per directory, and will onlt get events for directories where it was registered. It can also add weak listeners, which will be garbage collected when no other pointer to the listener exists.FileWatcher
is a wrapper around aDirWatcher
, and listens to files instead of directories. If the file listens to is a symbolic link, it will generate a full chain of symlink intermediaries, and listen to all of those, and if any of them changes, normalize that to a single file event on the origin symlink file.
import net.morimekta.file.DirWatcher;
import net.morimekta.file.FileWatcher;
import java.nio.file.Files;
import java.nio.file.Paths;
class MyWatcher implements FileEventListener {
void onFileEvent(Path file, FileEvent event) {
System.out.println(file.toString() + " " + event);
}
void main(String... args) {
var dirWatcher = new DirWatcher();
var fileWatcher = new FileWatcher(dirWatcher);
var path = Paths.get(args[0]);
if (Files.isDirecory(path)) {
dirWatcher.addWatcher(path, this);
} else {
fileWatcher.addWatcher(path, this);
}
Thread.sleep(Long.parseLong(args[1]));
System.exit(0);
}
}
Temporary Asset Folder
Wrapper around a temporary folder that will be deleted immediately when the util is closed. Handy when needing a set of temporary files for some operation that will be generated outside the programmers control.
Since normal temporary files will live until the app exits, especially in limited contexts it may have unintended side effects, e.g. on kubernetes temporary files are stored in on-memory-disk, so accumulating temporary files will eat memory.
import net.morimekta.file.TemporaryAssetFolder;
import java.io.IOException;
class FileHandler {
public Image reEncode(Image image, Format format) throws IOException {
try (var tmp = new TemporaryAssetFolder()) {
// add files to tmp, manipulate etc.
var tmpSource = tmp.resolveFile("source." + image.format.suffix);
image.writeTo(tmpSource);
var tmpTarget = tmp.resolveFile("target." + format.suffix);
var result = new SubProcessRunner().exec(
"ffmpeg", tmpSource.toString(), tmpTarget.toString());
if (result != 0) {
throw new IOException("failed to write " + tmpTarget);
}
return Image.readFrom(tmpTarget);
}
// all content of 'tmp' is deleted.
}
}
Utilities
Utility classes used both internally and can be used elsewhere.
FileUtil
readCanonicalPath(path)
: Read a canonical path directly from file system.
This essentially does the same as File.toCanonicalPath()
or
Path.toRealPath()
, but will never cache the resolved path and reuse that for
the next response. This is a problem when using an ongoing process to monitor
symbolic links and wanting the resolved content from underneath the hood. Java
will pretty aggressively cache resolved paths under the hood effectively making
symbolic links static. By using paths and this method you can enforce a new
read of the symbolic link to get the new target content instead of the old.
replaceSymbolicLink(link, newTarget)
: Handy when testing file events and
watching of symbolic links. This will replace a symbolic link with another, but
enforce that it only generates 1 event on the link itself.
Files.createSymbolicLink()
can only create where none exist, and a 'delete +
create' chain will make 2 file events.
list(path, recursive)
: List all files in a directory, including recursive
listing if specified.
deleteRecursively(path)
: Delete files and directories recursively.
PathUtil:
getFileBaseName(path)
: Get the file base name, which is the name of the
file without the file suffix. This will include leading '.' chars, but stop
before the last '.' with letters or numbers after itself.
getFileSuffix(path)
: Get the suffix of a file name. The suffix is the
content after the last '.' that has name content before itself. See
getFileBaseName()
.
getFileName(path)
: Get the file name from a path, which is the content
after the last '/' in the path. If the path ends with '/', it is empty.