Tentackle Common — The Foundation Module¶
Overview and Motivation¶
The tentackle-common module is the root of the entire Tentackle stack. Its package-info sums it up in one
line — "Common classes for build- and runtime" — and that dual role is the key to understanding it. Unlike every
other module, tentackle-common is used in two completely different contexts:
- At runtime, by every Tentackle application, as the lowest-level utility and SPI layer.
- At build time, by the Tentackle Maven plugins and the Wurbelizer, which need the same annotations, naming rules, money/date types, and the service-discovery primitives while generating code and analyzing annotations.
Because it must work everywhere, tentackle-common is deliberately dependency-free: it requires only
java.sql and java.compiler from the JDK and nothing else from Tentackle. Almost everything else in the framework
depends, directly or transitively, on this module.
Its single most important responsibility is the Service and Configuration API — Tentackle's replacement for
java.util.ServiceLoader that powers dependency injection, component replacement, and build-time annotation
analysis throughout the framework. That subsystem is large enough to have its own document; this page gives the
whole-module picture and points to it.
See also: services.md — the in-depth guide to the
@Service/@MappedServiceannotations, theServiceFinder/ServiceFactory, build-time@Analyzeprocessing, theModuleHookmechanism, and OSGi.
Design principles¶
- Zero framework dependencies. Nothing in Tentackle sits below this module, so it cannot depend on anything in Tentackle. It leans only on the JDK.
- Build-time and runtime. Types here are shared by the running application and by the Maven plugins/wurblets
that build it, which guarantees that, e.g., a
BMoney, a naming rule or a@Serviceregistration means the same thing in both worlds. - Annotation-driven, not reflection-at-startup. Component discovery is based on annotations analyzed at build
time and recorded under
META-INF, avoiding classpath scanning and minimizing startup cost. - Classpath and module-path. All mechanisms work identically in traditional classpath mode and in JPMS
modular mode (including jlink images), via the
ModuleHookindirection.
Key Concepts¶
Service and Configuration API¶
This is the heart of the module and is documented fully in services.md. In brief:
Service/ServiceName/MappedService— the annotations that register an implementation (a simple service inMETA-INF/services, or a mapped service inMETA-INF/mapped-servicesthat relates a property → class → value).ServiceFinder/DefaultServiceFinder/ServiceFinderKey— locate service configurations, preserving module-dependency order (the first URL belongs to the topmost module; the classpath comes last, unless the service is annotated with@ClasspathFirst).ServiceFactory— creates services and finders; a richer replacement forServiceLoader(any configuration type, per-service classloaders, modular plus non-modular). It is itself loadable viaServiceLoaderand therefore replaceable.Analyze— the meta-annotation that ties an annotation to a build-timeAnalyzeHandler(named by string, so the application never depends on the handler at runtime).AnnotationProcessor— a@Servicemeta-annotation registering the annotated class as ajavax.annotation.processing.Processor.ModuleHook/ModuleInfo/ModuleOrdinal/ModuleSorter— determine and topologically sort the module hierarchy (needed for resource bundles and for jlink images where module dependencies are otherwise not discoverable).ClasspathFirst— override a module-path service from the classpath.RemoteMethod/RecordDTO— annotations consumed by wurblets/analysis to make handwritten methods remoting-capable and to mark records for code generation.@RecordDTOdrives the DTO wurblet.
Isolated module layers¶
LayerSpec, LayerGraphSpec,
ModuleLayerBuilder and
LayerBootstrap build a graph of java.lang.ModuleLayers from a
declarative description, so that an application can run two versions of the same dependency side by side. The
service API is layer-aware via ServiceFactory.getServiceFinder(ModuleLayer, String). See layers.md.
Database-aware data types¶
Tentackle ships its own value types whose defining feature is stable database/serialization semantics across
timezones and optional immutability. Most are usable directly as model attribute types (mapped by
tentackle-sql data types).
BMoney— a money value derived fromBigDecimalwith a fixed, unchangeable scale; stored as aDOUBLEvalue +INTEGERscale. Only the arithmetic methods declared inBMoney(add,subtract,multiply(double),divide(double), …) return aBMoney; the inheritedBigDecimalmethods return a plainBigDecimal.toWords()spells the integral digits, nice for printing checks.DMoney— likeBMoney, but stored as aDECIMAL(scale 0) +INTEGERscale for exact decimal storage.Date— a date with database semantics: serialized asYYYYMMDDso it stays the same calendar date in every timezone (unlikejava.sql.Date, which serializes epochal time).Time— a time with database semantics: serialized asHHMMSSmmm.Timestamp— a timestamp with an optional UTC flag (the model's[UTC]option) that keeps it fixed to a timezone by convention rather than converting on serialization.Binary— stores a serializable object as a blob-like byte array using Java Object Serialization, but via binary-stream methods so it stays valid outside a transaction (unlike real blobs).TBinary— same asBinarybut uses the pluggableObjectSerializer(TRIP's serializer iftentackle-coreis present, see below), so the object need not implementSerializable.AbstractBinaryis the shared base.I18NText— a multilingual string usable as a model attribute (org.tentackle.sql.datatypes.I18NTextType); implementsCharSequence/Comparablefor the current locale.Freezable— the interface behind the "frozen" (made-immutable) capability of the date/time and binary types. See freezable.md for why it exists and how it makes old-school mutable types likejava.util.Datesafe as entity attributes.
Object serialization¶
ObjectSerializer abstracts generic object
serialization; DefaultObjectSerializer is the
JOS-based default. This is the seam that lets TBinary (and other framework code) serialize via TRIP instead of
plain Java serialization when tentackle-core is present.
Security and configuration¶
See cryptor.md for the full picture, including the build-time encryption of secrets into filtered resources and an honest account of what this protects against.
Cryptor— a simple symmetric en/decryptor. An application provides its own@Service(Cryptor.class)subclass with a confidential salt/passphrase; it is used to encrypt passwords in memory, during client/server login, and inbackend.properties.EncryptedProperties— aPropertiesvariant where values starting with~are decrypted via theCryptor(with\escaping and optional case-insensitive keys). This is the configuration format used for backend connection settings.Settings— global framework settings, such as the charset used to read and write files and by theCryptor.Constants— shared build- and runtime constants.Version— Tentackle version information (RELEASE,DATE), filtered intoVersion.propertiesat build time. Running it as a main class prints the version.
Internationalization and resource bundles¶
Resource-bundle handling differs between classpath and modular mode, so the framework routes it through its own factory. See bundle.md for the full i18n story.
Bundle— annotation marking a class that corresponds to a resource bundle.BundleFactory/DefaultBundleFactory/BundleSupport— locate and create bundles via the module hooks.LocaleProvider— supplies the current (thread-local)Localeand maps it to the locales the application supports.I18NText— the multilingual text value type (see above).CommonCommonBundle— the module's own message bundle.
Helper utilities¶
A set of static-method helper classes (not instantiable) and small utilities used throughout build and runtime:
StringHelper— string manipulation helpers. Among them,evaluate(String, Function)replaces@{name}/${name}placeholders, with optional defaults:@{name!default}applies the default if the variable is missing,@{name?default}also if it is blank. With${…}, the variable's value is evaluated again (nested variables, loops are detected), with@{…}it is taken as is. A missing variable without a default is left unchanged, and a backslash quotes the next character. The application bootstrap uses it to resolve system properties in backend properties.NumericEvaluator/NumericVariable— a small arithmetic expression evaluator: numeric literals, named variables,+ - * / %, unary signs and parentheses. The type parameter selects integer semantics (Integer,Long,BigInteger, …;/truncates) or floating-point semantics (Double,BigDecimal, …):
Integer port = new NumericEvaluator<>("(base + 1000) / 2", new NumericVariable<>("base", 20000)).evaluate();
Used for the ${port+1} arithmetic in derived service URIs,
and — as a Function<String,String> — as a converter of the
properties goal.
- StringNormalizer /
DefaultStringNormalizer — reduce strings to a
restricted character set (e.g., for normtext search); works for most Western languages by default.
- DateHelper — date/time helpers around java.util.Date:
conversions, locale-aware calendars (today()), and the derivation of a complete date from partial input,
such as a 4-digit year from a 2-digit year, the year from a month, or the month from a day, each within a
configurable window around the current date.
- FileHelper — file and resource helpers: loading properties
(loadProperties(name) tries the filesystem first, then the classpath, and returns EncryptedProperties),
copying files and directories, unzip with zip-slip protection, download, and openStreamUncached, which
avoids the JVM-wide jar URL cache (a closed classloader of a concurrent build would otherwise break the stream).
- ExceptionHelper — exception chain helpers: extracting an
exception of given types, handling it via Handlers, finding a temporary TentackleRuntimeException,
concatenating messages, and filtering noisy JDK and proxy frames from stack traces.
- Compare — null-safe Comparable comparisons.
- ParameterString — parses/builds name='value', …
parameter strings (used for the persisted form of I18NText, the message parameters of the modification log,
and the logging MDC). A name without a value denotes a flag.
- NamingRules — the replaceable singleton behind
all derived class names (domain/persistence/remote delegate types); see
naming-rules.md.
- StripMode — enum for string-stripping behavior, corresponding
to the bind options NOSTRIP, LSTRIP, RSTRIP and STRIP (see fromBindOption).
- Path / PathFactory
— a generic path abstraction and its factory.
External tools¶
ToolFinder— locates external executables.ToolRunner— runs external tools (used by build tooling, e.g., jlink/jpackage style invocations).
Exceptions¶
TentackleRuntimeException— the framework's base unchecked exception. It can be marked as temporary, meaning the cause may disappear and the operation may be retried later.InterruptedRuntimeException— usually wraps a checkedInterruptedException, and re-sets the thread's interrupted flag.
Package Layout¶
| Package | Contents |
|---|---|
org.tentackle.common |
Everything: the service/configuration API, the database-aware data types, object serialization, the Cryptor/EncryptedProperties security layer, i18n/bundle support, the helper utilities, external-tool support and the base exceptions. |
org.tentackle.common.service |
The Hook (ModuleHook) for this module. |
How It Fits Together¶
tentackle-common underpins the whole framework in two directions:
Build time Runtime
────────── ───────
tentackle-maven-plugin ┐ ┌ every Tentackle module
tentackle-sql-maven-… ├─ use ──►│ (core, session, sql, database,
Wurbelizer wurblets ┘ │ pdo, domain, persistence, fx, …)
│ │
└──────► tentackle-common ◄───┘
(annotations, ServiceFinder, money/date types,
naming rules, Cryptor, helpers — no deps)
- At build time, the Maven plugins read the annotations that are (directly or indirectly) meta-annotated with
@Analyze(@Service,@MappedServiceand the annotations built on them, such as@ClassIdfromtentackle-session, …) from source, run theirAnalyzeHandlers, and emit configuration files underMETA-INF. The same money/date/naming types are available, so the generated code is consistent. - At runtime, application code obtains implementations through
ServiceFactory.createService(...)/ServiceFactory.getServiceFinder(), which read thoseMETA-INFfiles in module-dependency order — working in both classpath and modular mode thanks to theModuleHooks every module provides.
A minimal application only needs a dependency on tentackle-common plus the tentackle-maven-plugin to use the
service API; higher-level utilities (e.g., the reflection-based class mappers, or TRIP serialization for
TBinary) additionally require tentackle-core.
Module Dependencies¶
tentackle-common is the bottom of the stack:
- It requires transitive only
java.sqlandjava.compilerfrom the JDK — and nothing from Tentackle. - It uses
org.tentackle.common.ModuleHook(to discover other modules'META-INFcontributions) andorg.tentackle.common.ServiceFactory(so an application can replace the factory itself). - It opens
org.tentackle.commontoorg.tentackle.corefor TRIP serialization. - It provides its own
ModuleHook(org.tentackle.common.service.Hook). - Almost every other Tentackle module depends on it, directly or transitively.
Related Documentation¶
- Services / ServiceFinder — the in-depth guide to the service and configuration API summarized above.
- Cryptor and EncryptedProperties — the in-depth guide to the secret-handling layer summarized above.
- Isolated Module Layers — running two versions of the same dependency side by side.
- Resource Bundles and Internationalization — bundles,
LocaleProviderandI18NText. - Naming Rules — the derived class names and how to replace the conventions.
- Freezable — why the date/time and binary value types are freezable, and how that protects entity attributes from aliasing mutation.
- Tentackle SQL — maps the data types here (
BMoney,DMoney,Date,Timestamp,I18NText, …) to database columns. - Session — uses
EncryptedProperties/Cryptorfor connection configuration and credentials. - PDO — the PDO pattern wired together by the service API.