Profile author reference
This .profile reference documents the supported Maven-based authoring subset for authors of custom JoinedWorkz Facilities.
Start with Build a custom Facility for a complete workflow. Application developers who consume existing Facilities should use Profiles and platforms.
1. Scope and terminology
The supported first authoring path covers:
- a
.profileresource in a separate Maven Facility JAR; - a package and an import of the Base Profile;
- a specialized replaceable Outlet;
- a Cartridge with an implementation class and bound Outlet;
- a Platform specialized from
Base; - applying the Cartridge to that Platform;
- the Properties-aware Java Cartridge entry point; and
- optional typed configuration properties.
Additional advanced contracts document scalar property Contributions with deprecation, alias and propagation metadata, plus Strategies that calculate Core Model properties during transformation. They do not broaden the first custom-Facility workflow to every Profile DSL construct.
The following terms are easy to confuse:
| Term | Meaning |
|---|---|
| Facility | Maven packaging and publication boundary; not a Profile DSL keyword |
| Profile | A .profile resource containing declarations |
| Platform | Named generation configuration selected by a CMN model |
| Cartridge | Profile declaration linked to a Java implementation |
| Cartridge application | The apply cartridge ... statement inside a Platform |
| Outlet | Named output destination supplied to a Cartridge |
There is no independent facility declaration in the Profile DSL. There is also no Profile application declaration corresponding to a CMN application: apply cartridge applies a Cartridge to a Platform.
2. File structure
The registered file extension is .profile. A custom Profile starts with one technical package and can then import definitions from another Profile:
package com.example.textreport.profile
import org.joinedworkz.facilities.common.profiles.baseThe package is the Profile namespace imported by a consuming CMN model. Give every Profile a stable, organization-owned package.
The first supported authoring path uses ordinary imports without aliases and a concrete package. Import aliases and abstract packages are outside this contract.
3. Outlets
An Outlet declaration has a name, an optional base Outlet and properties. It does not use a braced body:
outlet generatedTextReport specialization of StandardOutput
directory="./target/generated-text-report"StandardOutput is supplied by the Base Profile. Specializing it inherits the standard replaceable-output flags while the custom Outlet selects its own default directory.
The public Outlet properties used by built-in Facilities are:
| Property | Purpose |
|---|---|
directory | Default output root |
createNotExistingDirectory | Allow creation of a missing output directory |
overwriteExistingFiles | Allow replacement of existing generated files |
deleteFilesOnCleanBuild | Participate in JoinedWorkz clean-generation cleanup |
cleanWholdDirectoryOnCleanBuild | Clean the complete Outlet directory during clean generation |
keepLocalHistory | Request local-history handling where the build adapter supports it |
markAsDerived | Request derived-resource handling where the build adapter supports it |
cleanWholdDirectoryOnCleanBuild contains the historical spelling Whold; it is the actual property name.
These flags are Profile metadata. They do not add Maven Clean rules and do not decide whether files belong in Git. Use Generated output, ownership and regeneration for that decision.
Facilities can define additional Outlet names. This page is not a JoinedWorkz-wide Outlet registry. A Consumer can override documented directories through joinedworkz.properties.
4. Cartridges
A Cartridge declaration names its Java implementation and the Outlets available to it:
cartridge TextReportCartridge
implementation="com.example.textreport.TextReportCartridge"
outlets=generatedTextReportThe supported properties in this authoring path are:
implementation: the fully qualified name of anAbstractCartridgeimplementation available from the Consumer's compile classpath;outlets: one or more previously declared Outlets; andconfigurationProperties: optional typed keys owned by this Cartridge.
outlets=... only binds the available output destinations. It does not activate the Cartridge. The selected Platform must also contain:
platform TextReport {
apply cartridge TextReportCartridge
}An invalid implementation name is detected when generation tries to load the Cartridge, not merely when the Profile text is parsed. A Cartridge should also fail clearly if a required Outlet is absent.
Consumer projects can control an applied Cartridge through documented enablement properties or a CMN exclude clause. See Cartridge enablement for the precedence.
5. Platforms
A custom Platform can specialize one existing Platform and add its own Cartridge applications:
platform TextReport specialization of Base {
apply cartridge TextReportCartridge
}The specialized Platform inherits active behavior and the effective Setting from its base. TextReport therefore receives the Base wiring needed to instantiate the custom Cartridge and also retains active Base Cartridges.
The Consumer imports the custom Profile namespace and selects the Platform:
package com.example.consumer
import com.example.textreport.profile
platform TextReport
type Example {
}A Facility dependency makes the packaged Profile and implementation available; the import makes its names visible; platform TextReport selects its behavior. All three steps are required.
The first authoring contract covers one direct base Platform plus additional Cartridges. It does not define general multiple inheritance or arbitrary Profile-composition semantics.
6. Configuration properties
JoinedWorkz supports typed, first-class configuration properties for joinedworkz.properties:
'''Selects a custom generator mode.'''
configuration property CustomMode: ENUMERATION
key="custom.mode"
values="simple","extended"
default="simple"
audience="PUBLIC"
lifecycle="STABLE"The supported configuration types are:
STRINGINTEGERDECIMALENUMERATIONBOOLEAN
ID is a model-property type but is not valid for configuration properties.
A declaration can define:
key: an exact key or a parameterized key pattern;values: allowedENUMERATIONvalues;default: the declared fallback that the consuming implementation must apply when the property is absent;allowBlankandblankUsesDefault: the declared validation and consumption policy for an explicit blank;ignoreCase: case-insensitive enumeration validation;pattern: whether the key contains named parameters;audience:PUBLIC,ADVANCEDorINTERNAL; andlifecycle:STABLE,EXPERIMENTALorDEPRECATED.
With pattern=true, key contains one or more named <placeholder> segments, for example custom.route.<module>.<layer>. Every concrete joinedworkz.properties key must provide a non-empty value for every placeholder. A key with the pattern's literal structure but an empty placeholder stops generation with JW_CONFIG_INVALID_KEY instead of being reported as unknown. Generic runtime patterns retain their separately documented matching contracts.
Associate the declaration with its consuming Cartridge:
outlet generatedCustomSource
directory="./target/generated-custom"
cartridge CustomCartridge
implementation="com.example.CustomCartridge"
outlets=generatedCustomSource
configurationProperties=CustomModeThe key then belongs to the selected Platform's configuration contract. A disabled or excluded Cartridge keeps the declaration known but makes its consumer inactive, so Maven can report the configured key as unused.
Declaration and association register and validate the property; they do not implement its generation effect. The Cartridge or an injected helper must read the effective Properties object, apply the value and test the observable result. The minimal TextReport example does not define a custom property.
The registry does not materialize metadata into the Properties object. A missing key remains absent, an explicit blank remains an empty string and case-insensitive validation does not canonicalize the configured spelling. The consuming implementation must therefore apply its declared default, blank-use-default and case policy when resolving the raw value.
Facility authors must document their own key namespace, values, defaults, blank and case handling, lifecycle, output effect and minimum JoinedWorkz version. See Custom Facilities and additional properties for the complete validation and diagnostics contract.
7. Strategies and calculated properties
A Strategy calculates a model property while JoinedWorkz transforms CMN into the Core Model. Declare the implementation and associate it with a contributed property:
strategy TextReportLabelStrategy
implementation="com.example.textreport.TextReportLabelStrategy"
platform TextReport specialization of Base {
contribute to complextype {
property reportLabel: STRING strategy=TextReportLabelStrategy
}
}The implementation extends AbstractStrategy:
public Object apply(CmnObject cmnObject)For the documented Advanced contract, use Strategy properties on complextype or field. JoinedWorkz invokes the DI-managed implementation during transformation, before Cartridge preparation, validation and generation. A non-null result becomes a calculated CmnProperty; a local, explicitly supplied value with the same name remains authoritative. A null result leaves the property absent.
Cartridges and generators consume the resulting Core Model property. They do not call the Strategy again or duplicate its calculation. Strategy instances can be reused, so implementations must be stateless, side-effect-free and independent of execution order. The returned Java value must match the declared Profile property type.
The full lifecycle, DI boundary, current model-element limits and test requirements are in Profile Strategies and calculated properties.
8. Advanced property lifecycle metadata
Facility authors who already define Profile property Contributions can mark a scalar property as deprecated and optionally identify its canonical replacement:
package com.example.numeric.profile
import org.joinedworkz.facilities.common.profiles.base
platform Numeric specialization of Base {
contribute to simpletype<number> {
property oldLimit: INTEGER deprecated=true aliasOf=limit
property limit: INTEGER
}
}deprecated=true reports JW_DEPRECATED_PROPERTY when an applicable CMN model element uses oldLimit. aliasOf=limit additionally makes the converted value available as the canonical limit property. A deprecated property can omit aliasOf when the old and new behaviors must remain distinct during migration.
The declaration contract is:
deprecatedoccurs at most once and has one Boolean value;aliasOfoccurs at most once and references one scalar property in the same Contribution;- source and target types match, except that
INTEGERandDECIMALare also compatible with each other; - an alias does not declare its own
value,defaultValueorstrategy; - alias chains are allowed and resolve to their final canonical target; and
- every chain terminates at a non-alias property; cycles are invalid.
When a CMN element explicitly sets both alias and canonical property, equivalent converted values are accepted and still produce the deprecation warning. Different values report JW_PROPERTY_ALIAS_CONFLICT and stop generation before Cartridge output. Intermediate names in an alias chain are not introduced unless the CMN model explicitly uses them.
This metadata applies only when the contributed Profile property is applicable to the current model element and its active stereotypes. It does not rewrite CMN raw properties. The separate Profile-controlled property propagation reference defines how scalar property values can cross type, field, stereotype, method-type and content-reference relationships.
9. Java Cartridge contract
The public implementation entry point is:
public void apply(
CmnObject cmnObject,
Outlets outlets,
Properties joinedWorkzProperties)JoinedWorkz passes:
- the current transformed
CmnObject; - the Outlets bound to the Cartridge; and
- a non-null
Propertiesobject containing the loaded project entries.
The Properties object is empty when joinedworkz.properties does not exist. Profile-declared defaults and normalization are not inserted into it. The Cartridge selects the transformed object types it supports, resolves its required Outlets and orchestrates its own generators.
Write generated files through Outlet.generateFile(...). Use injected, replaceable generators and helpers instead of constructing fallback implementations in the Cartridge.
The complete Java and Maven example is in Build a custom Facility.
10. Packaging contract
The Facility JAR must contain:
- each
.profileresource in a location visible to the JoinedWorkz language loader; - the implementation class named by every public Cartridge or Strategy;
- the injectable helpers required by those implementations; and
- any Facility-owned templates or shared CMN resources used at generation time.
The Facility POM must separately declare:
- the directly required Core Facility and Core Model dependencies; and
- the Facility dependencies that supply imported Profiles or CMN models.
The supported Maven layout places Profile sources under model/ and declares that directory as a resource, so the Profile is packaged at the root of the JAR.
The Consumer must receive the Facility as an already built Reactor dependency, an installed artifact or a published artifact. Do not rely on compiling the Cartridge in the same module whose generate-sources execution needs to load it.
Keep the Maven plugin and every JoinedWorkz dependency on the same exact released version. A public Facility release or example must not depend on JoinedWorkz SNAPSHOT artifacts.
11. Constructs outside the first authoring contract
The Profile grammar and built-in Facilities contain additional constructs. They are outside the supported custom-Facility workflow documented here:
- custom Settings and arbitrary DI-module composition;
- Strategy use outside the documented calculated-property contract;
- property Overrides and Contribution behavior beyond the scalar lifecycle metadata documented above and the separately documented property-propagation contract;
- Redirections;
- custom Stereotypes, constraints and stereotype conventions;
- explicit source-tag calls from custom generators;
- import aliases and abstract packages; and
- Studio authoring and loading of custom Facilities.
Do not infer runtime behavior from grammar acceptance alone. These constructs require their own parser, runtime and end-to-end authoring examples before they can extend this public contract.
Framework grammar development, Xtext/EMF internals, build adapters and the JoinedWorkz release process remain maintainer concerns rather than Facility author APIs.
