Skip to content

Miscellaneous — Core Contracts, Collections, Pooling, and Utilities

Overview and Motivation

The package comment of org.tentackle.misc is disarmingly honest:

Miscellaneous stuff. Everything that doesn't fit elsewhere.

That undersells it. The package is not a junk drawer of leftovers — it holds several of the contracts the upper layers are built from, plus the collections, pooling and formatting machinery those layers depend on. The clearest evidence is PersistentObject, the persistence half of every PDO, which extends five interfaces defined here:

public interface PersistentObject<T extends PersistentDomainObject<T>>
       extends ..., Snapshotable<T>, Modifiable, SerialNumbered, ..., Immutable, ... {

So "is this object modified?", "give me a snapshot to revert to", "what is your id and serial?" and "can you be made read-only?" are all questions answered by contracts in org.tentackle.misc. They live here — rather than in the PDO layer — precisely because they are not about persistence: a plain value object, a UI model, or a collection can implement them just as well, and the framework's own collections do.

The rest of the package earns its place by being genuinely cross-cutting: the Pool that both database sessions and TRIP connections are managed by, the TrackedList that makes OR-mapping of collections possible, the locale-aware FormatHelper used in three dozen classes across the framework, and the SmartDateTimeParser in misc.time that turns ".+1d15h =paris" into tomorrow, 3pm local time, in Europe/Paris.

This document groups the package by what the classes are for. Two topics that live here are documented in depth elsewhere and are only cross-referenced below: Immutable and Snapshotable.

Core Contracts

Small interfaces that the rest of the framework implements. They are the reason the package sits so low in the dependency graph.

Interface Contract
Identifiable An object with an id (and thus a class + id identity). Extends Serializable.
SerialNumbered An Identifiable that also carries a long serial — a version, sequence or serial number.
Modifiable An object that can tell whether it has been modified.
Snapshotable<T> An object that can take snapshots and revert to them — see snapshot.md.
Immutable An object that can be switched read-only — see immutable.md.
Convertible A type that maps to and from another representation (toExternal / toInternal / getDefault) — how an enum or custom type becomes a database column via ConvertibleType.
ShortLongText An object offering both a short label and a longer description.
ScrollableResource<T> An AutoCloseable resource with a current row (1-based) — the abstraction behind database cursors.

Snapshotable deserves one clarification that is easy to get wrong: a snapshot is not a copy. It holds the information needed to revert the object to its state at the time the snapshot was taken; use copy() if you want an independent object.

Two functional interfaces round this out: Provider<T,E> and Acceptor<T,E> mirror Supplier and Consumer but permit a checked exception — the escape hatch you want when a lambda has to do I/O.

Collections

TrackedList — knowing what changed

TrackedList<E> (with the concrete TrackedArrayList) is a List that records its structural modifications: additions and removals are tracked separately, a replacement counts as a removal plus an addition, and every removed object is kept in a separate list.

That is exactly what OR-mapping needs. When an aggregate is saved, the persistence layer asks the list what happened: which elements are new (insert), which are gone (delete), which remain (update). Without the removal history, a deleted component would simply vanish from memory with no record that the row must go. The class is not restricted to PDOs, though — any list that needs a dirty flag can use it.

TrackedList also composes with the other contracts, which is what makes aggregates work as a unit:

  • it is Immutable — and setting the list immutable cascades to any elements that are themselves Immutable;
  • it is Snapshotable — with unlimited snapshots, cascading into Snapshotable elements, and reverting invalidates all later snapshots;
  • it can be configured to reject null elements outright;
  • TrackedListListener reports the events.

Copy-on-write wrappers

CopyOnWriteCollection, CopyOnWriteList and CopyOnWriteSet wrap an existing collection and clone it lazily before the first modification, leaving the original untouched.

Not the java.util.concurrent copy-on-write classes. Those are thread-safe and copy on every mutation. These are not thread-safe and copy once. The purpose is different: cheaply handing out a collection that the receiver may modify without disturbing the owner.

With iteratorModifying=false every operation works except Iterator.remove(); set it to true and iterator() copies the collection so the iterator can modify it.

Identifiable maps and keys

IdentifiableMap<T> maps Identifiables by a key composed of class and object id — necessary because ids are only unique per class, so a naive Map<Long,?> would collide across entity types. IdentifiableKey<T> is that composite key, usable for hashing or as a Comparable. The map starts as a hash map and switches itself to a tree map on the first call to getAllSorted(), trading a little speed for ordering only when ordering is actually asked for.

Immutable collections

ImmutableCollection, ImmutableArrayList and ImmutableException implement the switchable read-only model — see immutable.md for the full contract and how mutators are guarded.

Pooling

A small, complete pooling framework used by the framework's own expensive resources — DbPool for database sessions and AbstractConnectionPool for TRIP connections both build on it.

Type Role
Pool<T> The pool: get() / put(), sizes, timeouts, shutdown(), monitor()
Poolable<T> What a pooled resource must offer: its pool and its pool id
PoolSlot<T> One slot in the pool
AbstractPool / AbstractPoolSlot Base implementations: min/max sizes, fixed increments, idle timeouts
PoolTimeoutThread One thread for all pools, closing instances idle or in use too long

A poolable's poolId doubles as its state: 0 when idle in the pool, greater than 0 while lent out, -1 once removed. The timeout thread exists for long-running servers: releasing resources and capping memory matters more, over days of uptime, than keeping a warm connection around forever. Both an idle timeout (getIdleMinutes()) and a maximum usage time (getUsageMinutes()) are supported — the latter recycles resources that have been alive too long regardless of activity.

Formatting and Parsing

FormatHelper — localized patterns in one place

FormatHelper is the framework's most widely used misc class. It provides the localized format patterns for integers, floating-point numbers, money, dates, times and timestamps — each in a long and a short variant, and for temporals also with a zone offset or a time zone.

The patterns come from a resource bundle per locale, so they follow the i18n story rather than being hard-coded, and every getter exists both with an explicit Locale and without (using the current locale from the LocaleProvider). An application that dislikes a default overrides it once at startup with FormatHelper.applyPatterns(...), passing a Patterns record; every formatter in the application — including the FX components and table cells — picks the change up.

SmartDateTimeParser — typing dates the way people think

SmartDateTimeParser<T> in org.tentackle.misc.time parses any Temporal from input that a user would plausibly type. Instead of demanding a full, correctly formatted date, it accepts shorthand and relative expressions. The often-quoted example says it best:

".+1d15h =paris"   →   tomorrow, 15:00 local time, converted to Europe/Paris

Parsing runs in four steps:

  1. Text filters normalize the input — completing a two-digit year, expanding +2:30 to +02:30, resolving berlin to Europe/Berlin.
  2. Shortcuts modify the current value according to patterns found in the input, removing what they consume.
  3. If no shortcut applied, whatever is left goes to a regular DateTimeFormatter.
  4. If that throws, error handlers get a chance to repair the input and retry.

The built-in vocabulary:

Element Effect
NowShortcut Input starting with ., , or / means now
AddShortcut +10h adds ten hours; a bare -10 defaults to days (or hours for time-only types)
NumberShortcut 5h → 05:00:00.000; 7M → July 1st, midnight. Sets lesser fields to their minimum
ZoneShortcut / OffsetShortcut A zone or offset; prefixed with = it converts the value, otherwise it just sets it
DateFilter, ZoneFilter, OffsetFilter The normalizing filters
MissingTimeHandler, MissingZoneIdHandler, MissingOffsetHandler The error handlers that fill in what the input omitted

NumberShortcut's sliding window deserves a mention: 010120 becomes 2020-01-01 while 010199 becomes 1999-01-01, not 2099 — a WindowProvider decides where the century flips.

Applications can register their own filters and shortcuts. Most code, though, reaches for DateTimeUtilities — a @Service singleton wrapping preconfigured parsers with parseLocalDate, parseZonedDateTime, parseShortLocalTime and friends for the whole java.time family.

CSV and text

  • CsvConverter — a @Service converting values to and from delimiter-separated strings, RFC 4180 compliant. It is what the RDC table export writes when tentackle-fx-rdc-poi is absent.
  • SubString — a string segment described by its indexes, materialized lazily, so index arithmetic costs no string allocations.
  • CompoundValue — builds objects from strings: a constant, a $-prefixed dotted reference to a field/method (negatable with ! for booleans), or a script in Groovy or JRuby. Used for dynamic parameters in annotations — see CompoundValue for the full syntax and its role in validation.

Utilities

Class Purpose
ObjectUtilities Object helpers, mainly for CompoundValue conversions; a @Service, so applications extend it for their own types
DiagnosticUtilities Diagnostics (thread dumps and friends); also a @Service
ConcurrencyHelper Concurrency and multithreading helpers
TimeKeeper A nanoTime-based duration with a fluent API — used throughout for timing and statistics
Canonicalizer<T> Makes equal objects share one reference, cutting memory in large object graphs
CommandLine Parses --option / --option=value plus plain arguments
XUUID An id plus a UUID ("id-uuid"), so an external reference can be checked against brute-force guessing without changing the domain logic
NamedCounter / NamedValue<T> Thread-safe counters/values by name — e.g. numbering threads
InheritableThreadLocalHashTable A thread-local hashtable where each child thread gets its own copy
UserHomeJavaDir The per-user cache directory
IdSerialTuple / IdSerialNameTuple Id + serial (+ name) tuples, mainly for compact serialization

Holders

Holder<T> is a Consumer and Supplier in one — the standard trick for setting a value from inside a lambda when the enclosing method needs it back. VolatileHolder does the same with a volatile reference, and BlockingHolder turns it into a tiny handoff: one thread blocks in get() until another calls accept().

Properties

PropertiesUtilities (a @Service) loads java.util.Properties through PropertiesResource loaders registered with @PropertiesResourceService. Out of the box only DefaultPropertiesResource (file-based, priority 100) is present; an application can add loaders to override properties from environment variables, a YAML file, or anywhere else — see the service API.

PropertySupport, PropertyEvent and PropertyListener replace java.beans.PropertyChangeSupport and friends, which moved into the Swing desktop module in Java 9 — a dependency a headless server has no business carrying.

Package Layout

Group Types
Core contracts Identifiable, SerialNumbered, Modifiable, Snapshotable, Immutable, Convertible, ShortLongText, ScrollableResource, Provider, Acceptor
Collections TrackedList, TrackedArrayList, TrackedListListener, CopyOnWrite{Collection,List,Set}, IdentifiableMap, IdentifiableKey, Immutable{Collection,ArrayList}, ImmutableException
Pooling Pool, Poolable, PoolSlot, AbstractPool, AbstractPoolSlot, PoolTimeoutThread
Formatting FormatHelper, DateTimeUtilities, CsvConverter, SubString, CompoundValue
Utilities ObjectUtilities, DiagnosticUtilities, ConcurrencyHelper, TimeKeeper, Canonicalizer, CommandLine, XUUID, NamedCounter, NamedValue, InheritableThreadLocalHashTable, UserHomeJavaDir, IdSerialTuple, IdSerialNameTuple
Holders Holder, VolatileHolder, BlockingHolder
Properties PropertiesUtilities, PropertiesResource, DefaultPropertiesResource, PropertiesResourceService, PropertySupport, PropertyEvent, PropertyListener
misc.time SmartDateTimeParser, the shortcuts (Now, Add, Number, Zone, Offset), the filters (Date, Zone, Offset), the missing-value handlers