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 themselvesImmutable; - it is
Snapshotable— with unlimited snapshots, cascading intoSnapshotableelements, and reverting invalidates all later snapshots; - it can be configured to reject
nullelements outright; TrackedListListenerreports 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.concurrentcopy-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:
Parsing runs in four steps:
- Text filters normalize the input — completing a two-digit year, expanding
+2:30to+02:30, resolvingberlintoEurope/Berlin. - Shortcuts modify the current value according to patterns found in the input, removing what they consume.
- If no shortcut applied, whatever is left goes to a regular
DateTimeFormatter. - 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@Serviceconverting 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 |
Related Documentation¶
- Tentackle Core — the runtime foundation this package belongs to.
- CompoundValue —
CompoundValueandObjectUtilitiesin depth. - Immutable — the switchable read-only contract and its collections.
- Snapshots and Copies —
SnapshotableandTrackedListsnapshot semantics in depth. - PDO —
PersistentObjectimplements the contracts defined here. - Resource Bundles — where
FormatHelper's localized patterns come from. - Service and Configuration API — how the
@Servicesingletons here are replaced. - Tentackle Database — session pooling and cursors in practice.