The synchronizer model
This is the single most important architectural pattern in Dirigible. The platform does not "deploy" projects - files under /registry/public/... are continuously reconciled into runtime state by synchronizers.
Understanding this model is mandatory before adding a new artefact type, changing an engine, or debugging why a file is not picked up.
Three collaborating pieces per artefact type
For every reconciled artefact type the platform provides:
- An
ArtefactJPA entity - a class extendingArtefact(components/core/core-base/.../artefact/Artefact.java). It is the persisted projection of the file: location, hash, lifecycle phase, plus type-specific columns. - A synchronizer - a Spring bean extending
BaseSynchronizer<A, ID>(single-tenant) orMultitenantBaseSynchronizer<A, ID>. It scans the repository for a file extension or pattern, parses each match, upserts the entity, and reacts to lifecycle events. - An engine, service, or endpoint - the runtime consumer of the live artefact. Quartz for
.job, Flowable for.bpmn, Camel for.camel, Spring MVC for*Controller.ts, etc.
Existing implementations (grep extends BaseSynchronizer / extends MultitenantBaseSynchronizer in the codebase) give the full inventory: Job, Bpmn, Camel, Listener, Csvim, DataSource, Table, View, Schema, Access, Roles, Expose, ExtensionPoint, Extension, Markdown, Confluence, Proxy, Websocket, OData, OpenAPI, Component, Entity, Java.
Lifecycle phases
Each artefact moves through a fixed set of phases, driven by the synchronizer:
- CREATE - artefact discovered for the first time.
- UPDATE - artefact's content hash changed.
- DELETE - file removed from the repository.
- START - bring the live runtime state up (register the Quartz job, deploy the Flowable process, install the Camel route, etc.).
- STOP - tear it down (used on DELETE, and before START on UPDATE).
A typical UPDATE pass is STOP (on the previous version) then START (on the new content). Engine implementations encode this in their concrete synchronizer.
Ordering
Synchronizer execution order is fixed across types via constants in SynchronizersOrder. This matters for cross-artefact dependencies - a *.table must materialize before a *.csvim references it, a *.datasource must exist before a *.table targets it, and so on.
Within a single artefact type, ordering is governed by per-entity topological sort when an artefact declares dependencies (see TopologicallySortable / TopologicallyDepletable in core-base).
Cadence
The synchronizer thread polls on a fixed cadence, configurable via DIRIGIBLE_SYNCHRONIZER_FREQUENCY. Between cycles, files are at rest on disk; the platform does not watch the filesystem aggressively (the local watcher in core-initializers covers external mounts, not the default registry).
For tests or scripted flows that need a synchronous reconciliation, call SynchronizationProcessor.forceProcessSynchronizers(). This is the path taken by HTTP-only integration tests (JavaEngineIT and friends); it is not the normal path.
Adding a new artefact type
To support a new file extension end to end:
- Define the entity - a class extending
Artefactwith a JPA@Entitymapping under one of the JPA scan packages (org.eclipse.dirigible.componentsororg.eclipse.dirigible.engine). - Implement the synchronizer - extend
BaseSynchronizer(orMultitenantBaseSynchronizerif the artefact should be tenant-isolated). OverrideparseImpl,completeImpl, and the lifecycle hooks. Register it as a Spring@Component. - Wire the engine - whatever actually does work with the live artefact (a Spring service, a scheduled task, a Camel processor, a controller).
- Register the module - add the component module to the matching
components/group/group-*/pom.xmlaggregator so the assembly picks it up.
The four parts together let the platform reconcile the new artefact type without further configuration.
Special case: JavaScript and TypeScript
JS and TS user code is not synchronized. JavascriptEndpoint and TypeScriptEndpoint load modules on demand from the repository when a /services/js/... or /services/ts/... URL is hit. Save a JS file - it is live immediately on the next request. See Lifecycle and hot-reload.
Special case: client Java
Client .java files are synchronized, but the heavy work (one javac task, one fresh ClientClassLoader, consumer fan-out) happens inside JavaSynchronizer.finishing(). The JavaClassConsumer SPI plugs custom behavior into that fan-out - see Extensibility.
Reference
Artefact-components/core/core-base/.../artefact/Artefact.javaBaseSynchronizer,MultitenantBaseSynchronizer-components/core/core-base/.../synchronizer/SynchronizersOrder-components/core/core-base/.../synchronizer/SynchronizersOrder.javaSynchronizationProcessor-components/core/core-initializers/