Java examples
CDM 7.4.0The pieces every CDM integration needs, as code you can paste into a project: building objects, moving them in and out of JSON, validating and qualifying them, calling the model's own functions, and the post-processing that makes a document internally consistent.
Maven setup
Two ways in: a new project pulling cdm-java from Maven Central, or a module inside the CDM source tree inheriting cdm-parent. Build with JDK 21 — the CDM enforces [21,22) — while the bytecode targets Java 11. Adding a module to the tree also means adding it to the root pom’s <modules>, or the reactor skips it.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>standalone-cdm-project</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<cdm.version>7.4.0</cdm.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.finos.cdm</groupId>
<artifactId>cdm-java</artifactId>
<version>${cdm.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.finos.cdm</groupId>
<artifactId>cdm-java</artifactId>
</dependency>
</dependencies>
</project> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.finos.cdm</groupId>
<artifactId>cdm-parent</artifactId>
<version>0.0.0.master-SNAPSHOT</version>
</parent>
<artifactId>your.example</artifactId>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.finos.cdm</groupId>
<artifactId>cdm-java</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.finos.cdm</groupId>
<artifactId>cdm-java</artifactId>
</dependency>
<dependency>
<groupId>org.finos.rune-common</groupId>
<artifactId>rune-common</artifactId>
</dependency>
<dependency>
<groupId>org.finos.rune-testing</groupId>
<artifactId>rune-testing</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
</dependencies>
</project> Party builder
Two parties — a legal entity with an LEI, an account and a business unit, and a natural person with a national identity number. Each carries its own external key, so both can sit in one document without their references colliding.
//Party Legal Entity
Party party = Party.builder()
.setMeta(MetaFields.builder()
.setExternalKey("partyKey123"))
.setPartyId(List.of(PartyIdentifier.builder()
.setIdentifierType(PartyIdentifierTypeEnum.LEI)
.setIdentifier(FieldWithMetaString.builder()
.setMeta(MetaFields.builder()
.setExternalKey("partyIdKey123"))
.setValue("PartyLEI123"))))
.setAccount(Account.builder()
.setAccountNumber(FieldWithMetaString.builder()
.setValue("PartyAccountNumber"))
.setAccountName(FieldWithMetaString.builder()
.setValue("PartyAccountName")))
.setNameValue("PartyName123")
.setBusinessUnit(List.of(BusinessUnit.builder()
.setName("NY_BANK_CAPITAL_MARKETS")
.setIdentifier(Identifier.builder()
.setAssignedIdentifier(List.of(AssignedIdentifier.builder()
.setIdentifierValue("NY_BANK_CAPITAL_MARKETS_IDENTIFIER")))
.setIssuer(FieldWithMetaString.builder()
.setValue("NY_BANK")))
.setContactInformation(ContactInformation.builder()
.setAddress(List.of(Address.builder()
.setCountry(
FieldWithMetaString.builder()
.setValue("United States"))
.setStreet(List.of("1 Wall Street"))
.setCity("New York")
.setState("NY")
.setPostalCode("10001"))))))
.build();
//Party as Person
Party person = Party.builder()
.setMeta(MetaFields.builder()
.setExternalKey("partyKey456"))
.setPartyId(List.of(PartyIdentifier.builder()
.setIdentifierType(PartyIdentifierTypeEnum.LEI)
.setIdentifier(FieldWithMetaString.builder()
.setMeta(MetaFields.builder()
.setExternalKey("partyIdKey456"))
.setValue("PartyLEI456"))))
.setPerson(List.of(NaturalPerson.builder()
.setPersonId(List.of(FieldWithMetaPersonIdentifier.builder()
.setValue(PersonIdentifier.builder()
.setIdentifierType(PersonIdentifierTypeEnum.NIDN)
.setIdentifierValue("ID123"))))
.setFirstName("FirstName")
.setSurname("SurName")
.setContactInformation(ContactInformation.builder()
.setAddress(List.of(Address.builder()
.setCountry(
FieldWithMetaString.builder()
.setValue("United States"))
.setStreet(List.of("1 Market Street"))
.setCity("San Francisco")
.setState("CA")
.setPostalCode("94105"))))))
.setBusinessUnit(List.of(BusinessUnit.builder()
.setName("SF_BANK_CAPITAL_MARKETS")
.setIdentifier(Identifier.builder()
.setAssignedIdentifier(List.of(AssignedIdentifier.builder()
.setIdentifierValue("SF_BANK_CAPITAL_MARKETS_IDENTIFIER")))
.setIssuer(FieldWithMetaString.builder()
.setValue("SF_BANK")))
.setContactInformation(ContactInformation.builder()
.setAddress(List.of(Address.builder()
.setCountry(
FieldWithMetaString.builder()
.setValue("United States"))
.setStreet(List.of("1 Market Street"))
.setCity("San Francisco")
.setState("CA")
.setPostalCode("94105"))))))
.build(); Imports (16)
import cdm.base.staticdata.identifier.AssignedIdentifier;
import cdm.base.staticdata.identifier.Identifier;
import cdm.base.staticdata.party.Account;
import cdm.base.staticdata.party.Address;
import cdm.base.staticdata.party.BusinessUnit;
import cdm.base.staticdata.party.ContactInformation;
import cdm.base.staticdata.party.NaturalPerson;
import cdm.base.staticdata.party.Party;
import cdm.base.staticdata.party.PartyIdentifier;
import cdm.base.staticdata.party.PartyIdentifierTypeEnum;
import cdm.base.staticdata.party.PersonIdentifier;
import cdm.base.staticdata.party.PersonIdentifierTypeEnum;
import cdm.base.staticdata.party.metafields.FieldWithMetaPersonIdentifier;
import com.rosetta.model.metafields.FieldWithMetaString;
import com.rosetta.model.metafields.MetaFields;
import java.util.List; Types used: Party · PartyIdentifier · Account · BusinessUnit · Identifier · NaturalPerson · PersonIdentifier · Address
Serialization and deserialization
Round-trips an existing tradeState through JSON. Both calls throw JsonProcessingException.
//Serialize
RuneJsonObjectMapper runeJsonObjectMapper = new RuneJsonObjectMapper();
String tradeStateJson = runeJsonObjectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(tradeState);
//Deserialize
TradeState tsObj = runeJsonObjectMapper.readValue(tradeStateJson, TradeState.class); Imports (2)
import cdm.event.common.TradeState;
import org.finos.rune.mapper.RuneJsonObjectMapper; Types used: TradeState
Validation
Runs the model’s own validation rules over an object and reports what failed. Needs a Guice injector built from CdmRuntimeModule.
RosettaTypeValidator validateProcessor = injector.getInstance(RosettaTypeValidator.class);
ValidationReport validationReport = validateProcessor.runProcessStep(BusinessEvent.class, businessEvent);
validationReport.logReport();
if (validationReport.success()) {
System.out.println("Validator Success");
} else {
validationReport.validationFailures().forEach(f ->
System.out.println(f.getName() + ": " + f.getFailureReason()));
} Imports (3)
import cdm.event.common.BusinessEvent;
import com.regnosys.rosetta.common.validation.RosettaTypeValidator;
import com.regnosys.rosetta.common.validation.ValidationReport; Types used: BusinessEvent
Qualification
Asks the model what an object is — which product or event type its economics match — and writes the answer back into the object. Takes a builder rather than a built object, so call toBuilder() on the way in; wfs here is a WorkflowStep from a function call. Needs the same Guice injector as validation.
// Qualify
QualifyProcessorStep qualifyProcessor = injector.getInstance(QualifyProcessorStep.class);
QualificationReport report = qualifyProcessor.runProcessStep(WorkflowStep.class, wfs.toBuilder());
report.logReport();
WorkflowStep qualifiedWorkflowStep = (WorkflowStep) report.getResultObject().build();
RuneJsonObjectMapper objectMapper = new RuneJsonObjectMapper();
LOGGER.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(qualifiedWorkflowStep)); Imports (4)
import cdm.event.workflow.WorkflowStep;
import com.regnosys.rosetta.common.postprocess.qualify.QualificationReport;
import com.regnosys.rosetta.common.postprocess.qualify.QualifyProcessorStep;
import org.finos.rune.mapper.RuneJsonObjectMapper; Types used: WorkflowStep
Execute a Rune function
Calls one of the model’s own functions. Create_WorkflowStep takes eight arguments in declaration order — message information, timestamps, identifiers, parties, accounts, the previous step, the action and the business event — and optional ones can be null.
Create_WorkflowStep createWorkflowStep = new Create_WorkflowStep.Create_WorkflowStepDefault();
injector.injectMembers(createWorkflowStep);
WorkflowStep wfs = createWorkflowStep.evaluate(null, eventTimestampList,
identifierList, null, null, null, ActionEnum.NEW, businessEventResult);
//Action types New, Cancel, Correct Imports (3)
import cdm.event.common.ActionEnum;
import cdm.event.workflow.WorkflowStep;
import cdm.event.workflow.functions.Create_WorkflowStep; Types used: WorkflowStep · BusinessEvent · EventTimestamp · Identifier · ActionEnum
Post-processing
Run after a function returns and before build(). Four steps in order, mutating the builder in place: rekey, update temporary keys, resolve references, qualify. Covers on its own what the Rekey and Qualification sections do a step at a time — calling those as well as this one repeats work. Despite the name, WorkflowPostProcessor is not WorkflowStep-specific: it takes any root type, a Trade here. PostProcessor is an interface and nothing in CdmRuntimeModule binds it, hence the module — ask for WorkflowPostProcessor by its concrete class instead and you can drop it.
Injector injector = Guice.createInjector(new CdmRuntimeModule(), new AbstractModule() {
@Override
protected void configure() {
bind(PostProcessor.class).to(WorkflowPostProcessor.class);
}
});
PostProcessor postProcessor = injector.getInstance(PostProcessor.class);
Trade.TradeBuilder builder = trade.toBuilder();
postProcessor.postProcess(Trade.class, builder);
Trade processed = builder.build(); Imports (7)
import cdm.event.common.Trade;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.regnosys.rosetta.common.postprocess.WorkflowPostProcessor;
import com.rosetta.model.lib.process.PostProcessor;
import org.finos.cdm.CdmRuntimeModule; Types used: Trade
Rekey
Generates the global key on every object that declares [metadata key], then repoints external references at the keys it generated. Neither step is injectable — construct them. Takes a builder, not a built object. This is the first thing post-processing does, so call it on its own only when you want the keys without the rest of the chain.
ReKeyProcessStep reKey = new ReKeyProcessStep(new GlobalKeyProcessStep(NonNullHashCollector::new));
WorkflowStep.WorkflowStepBuilder builder = wfs.toBuilder();
reKey.runProcessStep(WorkflowStep.class, builder);
WorkflowStep wfsRekeyed = builder.build(); Imports (4)
import cdm.event.workflow.WorkflowStep;
import com.regnosys.rosetta.common.hashing.GlobalKeyProcessStep;
import com.regnosys.rosetta.common.hashing.NonNullHashCollector;
import com.regnosys.rosetta.common.hashing.ReKeyProcessStep; Types used: WorkflowStep
Run these against any CDM release
Workbench loads 200+ releases and generates the Java for each, so you can see what a version change does to this code before you commit to it.