A library to help with JRE stuff like shutdownhooks or reading resources.
This package contains several collections I've come across and used in some programs.
A synchronized, size-limited FIFO-queue.
When it is full, an insertion discards the head. offerAndGetEvicted returns the discarded element (or null), size() and isFull() tell how much room is left.
A normal HashMap, but limited in size. If there is a size-overflow, the oldest value will be discarded.
A synchronized, size-limited HashMap.
This is a data-structure that holds arbitrary objects in a DataQueue and you may or may not add one or many indexes, which will generate a DataMap for each index, using the given keySupplier to generate the key for each entry.
When you add a new element, delete an element or clear the list, all indexes will be automatically equally affected.
An element that leaves the table -- evicted by an add to a full table, polled, or removed through any index with remove or multiRemove -- leaves the queue and every index.
An index entry that was taken over by a newer element with the same key stays.
An add to a full table discards the oldest elements. Use addAndGetEvicted to learn which ones:
DataTable<String> dt = new DataTable<>(String.class, 2);
dt.add("a", "b");
List<String> lost = dt.addAndGetEvicted("c"); // ["a"]The indexes use minimal memory, since the references to the corresponding values is shared.
DataTable<String> dt = new DataTable<>(String.class, 10);
dt.addIndex("index1", e -> e);
dt.addIndex("index2", e -> e);
dt.add("test");
assertThat(dt.get("index1", "test")).isEqualTo("test");
assertThat(dt.get("index2", "test")).isEqualTo("test");This is a data-structure that uses two files to persist itself. This way you have a file left, if something happens during persisting. It is not thread-safe; synchronize access yourself if you share an instance.
DoubleBufferedFile dbf = new DoubleBufferedFile(Path.of("new"), "txt");
dbf.write(w -> w.write("test_old"));
dbf.write(w -> w.write("test_new"));
String value = dbf.read(); // is equal to "test_new"
String fallback = dbf.readOther(); // is equal to "test_old"
dbf.delete();The above example generates two files: new1.txt and new2.txt and the accessors always give the correct file-handle to read or write-to.
write() returns only after the content and the directory entry are on the storage device (fsync), so a power loss after it returned cannot leave the file empty or truncated. On slow media like SD cards this can take tens to hundreds of milliseconds per write.
By default the file with the later modification time is the newer one. On a machine whose clock can be set backwards (no real-time clock, NTP correcting after boot) that can make the older content win.
If your content carries a sequence number that increases on every write, pass a version reader. The file with the higher version is then the newer one for read(), write() and readOther(); a file without a version (the reader returns null or throws, or the file can't be read) loses against a versioned one, and equal versions fall back to the clock.
// sequenceOf(String) parses the content and returns its sequence number, or null if it can't
DoubleBufferedFile dbf = new DoubleBufferedFile(Path.of("db"), "json", State::sequenceOf);
dbf.write(w -> w.write("{\"sequence\":1}"));
dbf.write(w -> w.write("{\"sequence\":2}"));
Long version = dbf.getNewestVersion(); // is 2The version of a file is read again only after the file changed (modification time or size).
Offers some often used date-time conversions including UTC and ISO8601.
This helps you to load and instantiate classes that haven't already been loaded using a given class-loader. This is of great help to strip boilerplate code when doing stuff like plugin-systems or the like.
Helps reading and searching for resource files.
The special thing about these is, that they work in JAR-files AND server-deployments alike.
If you'd like some code running before the Java-VM shuts down, then this is the way to go. I use it for shutting down the EntityManagerFactory in some programs as gracefully as it gets.
Shut down the executor-service gracefully at the end of your program.
ShutdownHook.register(() -> {
executorService.shutdown();
try {
if (!executorService.awaitTermination(1, TimeUnit.SECONDS))
executorService.shutdownNow();
} catch (InterruptedException e) {
executorService.shutdownNow();
}
});Reflection utilities. If you'd like to scan for a specific class-type within an object tree, then this could be of help. Same if you'd like to get the instance of such a field within an instance, but get it by path-name.
// getFieldByPath
TestClass tc = new TestClass();
MyType mt = Reflecting.getFieldByPath("subClass.myType", tc, ContainsMyType.class);
mt.setName("blubb");
assertThat(tc.getSubClass().getMyType().getName()).isEqualTo("blubb");
assertThat(tc.getMyType().getName()).isNull();
// getPathsOf
List<String> results = Reflecting.getPathsOf(TestClass.class, MyType.class, ContainsMyType.class);
assertThat(results).contains("myType");
assertThat(results).contains("subClass.myType");This utility-class contains swallow and swallowReturning which helps you to swallow a specific exception (checked or unchecked) silently.
@Test
public void BeforeTest() {
try {
// Some method you don't have control over throwing an exception.
throw new IllegalArgumentException();
} catch (IllegalArgumentException e) {
// NOOP.
}
}The problem with this is that it's very verbose, possibly destroys the good visual style you're currently running in your library, or that your linter complains about NOOP catches.
Exceptions.swallow(() -> {
throw new IllegalArgumentException();
}, IllegalArgumentException.class);Obviously, you can use swallowReturning for methods that return a value and throw a checked or unchecked exception.
The Exception parameter is a varArg, so you may specify more than one Exception here.