Generate Java code from DAML

Introduction

When writing applications for the ledger in Java, you want to work with a representation of DAML templates and data types in Java that closely resemble the original DAML code while still being as true to the native types in Java as possible. To achieve this, you can use DAML to Java code generator (“Java codegen”) to generate Java types based on a DAML model. You can then use these types in your Java code when reading information from and sending data to the ledger.

Download

You can download the latest version of the Java codegen. Make sure that the following versions are aligned:

  • the downloaded Java codegen jar file, eg. x.y.z
  • the dependency to bindings-java, eg. x.y.z
  • the sdk-version attribute in the daml.yaml file, eg. x.y.z

Run the Java codegen

The Java codegen takes DAML archive (DAR) files as input and generates Java files for DAML templates, records, and variants. For information on creating DAR files see Building DAML projects. To use the Java codegen, run this command in a terminal:

java -jar <path-to-codegen-jar>

Use this command to display the help text:

java -jar codegen.jar --help

Generate Java code from DAR files

Pass one or more DAR files as arguments to the Java codegen. Use the -o or --output-directory parameter for specifying the directory for the generated Java files.

java -jar java-codegen.jar -o target/generated-sources/daml daml/my-project.dar
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

To avoid possible name clashes in the generated Java sources, you should specify a Java package prefix for each input file:

java -jar java-codegen.jar -o target/generated-sources/daml \
    daml/project1.dar=com.example.daml.project1 \
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    daml/project2.dar=com.example.daml.project2
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^

Generate the decoder utility class

When reading transactions from the ledger, you typically want to convert a CreatedEvent from the Ledger API to the corresponding generated Contract class. The Java codegen can optionally generate a decoder class based on the input DAR files that calls the fromCreatedEvent method of the respective generated Contract class (see Templates). The decoder class can do this for all templates in the input DAR files.

To generate such a decoder class, provide the command line parameter -d or --decoderClass with a fully qualified class name:

java -jar java-codegen.jar -o target/generated-sources/daml \
    -d com.myproject.DamModelDecoder daml/my-project.dar
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Receive feedback

By default, the logging is configured so that you’ll only see error messages.

If you want to change this behavior, you can ask to receive more extensive feedback using the -V or --verbosity command-line option. This option takes a numeric parameter from 0 to 4, where 0 corresponds to the default quiet behavior and 4 represents the most verbose output possible.

In the following example the logging is set to print most of the output with detailed debugging information:

java -jar java-codegen.jar -o target/generated-sources/daml -V 3
                                                            ^^^^

Integrate with build tools

While we currently don’t provide direct integration with Maven, Groovy, SBT, etc., you can run the Java codegen as described in Run the Java codegen just like any other external process (for example the protobuf compiler). Alternatively you can integrate it as a runnable dependency in your pom.xml file for Maven.

The following snippet is an excerpt from the pom.xml that is part of the IOU Quickstart Tutorial guide.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.6.0</version>
    <dependencies>
        <dependency>
            <groupId>com.daml</groupId>
            <artifactId>codegen-java</artifactId>
            <version>__VERSION__</version>
            <type>jar</type>
        </dependency>
    </dependencies>
    <executions>
        <execution>
            <id>daml-codegen-java</id>
            <phase>generate-sources</phase>
            <goals>
                <goal>java</goal>
            </goals>
            <configuration>
                <includeProjectDependencies>false</includeProjectDependencies>
                <includePluginDependencies>true</includePluginDependencies>
                <mainClass>com.daml.lf.codegen.Main</mainClass>
                <arguments>
                    <argument>-o</argument>
                    <argument>${daml-codegen-java.output}</argument>
                    <argument>-d</argument>
                    <argument>com.daml.quickstart.iou.TemplateDecoder</argument>
                    <argument>${project.basedir}/.daml/dist/quickstart-0.0.1.dar=com.daml.quickstart.model</argument>
                </arguments>
            </configuration>
        </execution>
    </executions>
</plugin>

Compile the generated Java code

To compile the generated Java code, add the Java Bindings library with the same version as the Java codegen to the classpath.

With Maven you can do this by adding a dependency to the pom.xml file:

<dependency>
    <groupId>com.daml</groupId>
    <artifactId>bindings-rxjava</artifactId>
    <version>x.y.z</version>
</dependency>

Understand the generated Java model

The Java codegen generates source files in a directory tree under the output directory specified on the command line.

Map DAML primitives to Java types

DAML built-in types are translated to the following equivalent types in Java:

DAML type Java type Java Bindings Value Type
Int java.lang.Long Int64
Numeric java.math.BigDecimal Numeric
Text java.lang.String Text
Bool java.util.Boolean Bool
Party java.lang.String Party
Date java.time.LocalDate Date
Time java.time.Instant Timestamp
List or [] java.util.List DamlList
TextMap java.util.Map Restricted to using String keys. DamlTextMap
Optional java.util.Optional DamlOptional
() (Unit) None since the Java language doesn’t have a direct equivalent of DAML’s Unit type (), the generated code uses the Java Bindings value type. Unit
ContractId Fields of type ContractId X refer to the generated ContractId class of the respective template X. ContractId

Understand escaping rules

To avoid clashes with Java keywords, the Java codegen applies escaping rules to the following DAML identifiers:

  • Type names (except the already mapped built-in types)
  • Constructor names
  • Type parameters
  • Module names
  • Field names

If any of these identifiers match one of the Java reserved keywords, the Java codegen appends a dollar sign $ to the name. For example, a field with the name import will be generated as a Java field with the name import$.

Understand the generated classes

Every user-defined data type in DAML (template, record, and variant) is represented by one or more Java classes as described in this section.

The Java package for the generated classes is the equivalent of the lowercase DAML module name.

DAML
module Foo.Bar.Baz where
Java
package foo.bar.baz;

Records (a.k.a product types)

A DAML record is represented by a Java class with fields that have the same name as the DAML record fields. A DAML field having the type of another record is represented as a field having the type of the generated class for that record.

Com/Acme/ProductTypes.daml
module Com.Acme.ProductTypes where

data Person = Person with name : Name; age : Decimal
data Name = Name with firstName : Text; lastName : Text

A Java file is generated that defines the class for the type Person:

com/acme/producttypes/Person.java
package com.acme.producttypes;

public class Person {
  public final Name name;
  public final BigDecimal age;

  public static Person fromValue(Value value$) { /* ... */ }

  public Person(Name name, BigDecimal age) { /* ... */ }
  public Record toValue() { /* ... */ }
}

A Java file is generated that defines the class for the type Name:

com/acme/producttypes.Name.java
package com.acme.producttypes;

public class Name {
  public final String fistName;
  public final String lastName;

  public static Person fromValue(Value value$) { /* ... */ }

  public Name(String fistName, String lastName) { /* ... */ }
  public Record toValue() { /* ... */ }
}

Templates

The Java codegen generates three classes for a DAML template:

TemplateName
Represents the contract data or the template fields.
TemplateName.ContractId
Used whenever a contract ID of the corresponding template is used in another template or record, for example: data Foo = Foo (ContractId Bar). This class also provides methods to generate an ExerciseCommand for each choice that can be sent to the ledger with the Java Bindings. .. TODO: refer to another section explaining exactly that, when we have it.
TemplateName.Contract
Represents an actual contract on the ledger. It contains a field for the contract ID (of type TemplateName.ContractId) and a field for the template data (of type TemplateName). With the static method TemplateName.Contract.fromCreatedEvent, you can deserialize a CreatedEvent to an instance of TemplateName.Contract.
Com/Acme/Templates.daml
module Com.Acme.Templates where

data BarKey =
  BarKey
    with
      p : Party
      t : Text

template Bar
  with
    owner: Party
    name: Text
  where
    signatory owner

    key BarKey owner name : BarKey
    maintainer key.p
    
    controller owner can
      Bar_SomeChoice: Bool
        with
        aName: Text
          do return True

A file is generated that defines three Java classes:

  1. Bar
  2. Bar.ContractId
  3. Bar.Contract
com/acme/templates/Bar.java
package com.acme.templates;

public class Bar extends Template {

  public static final Identifier TEMPLATE_ID = new Identifier("some-package-id", "Com.Acme.Templates", "Bar");

  public final String owner;
  public final String name;

  public static ExerciseByKeyCommand exerciseByKeyBar_SomeChoice(BarKey key, Bar_SomeChoice arg) { /* ... */ }

  public static ExerciseByKeyCommand exerciseByKeyBar_SomeChoice(BarKey key, String aName) { /* ... */ }

  public CreateAndExerciseCommand createAndExerciseBar_SomeChoice(Bar_SomeChoice arg) { /* ... */ }

  public CreateAndExerciseCommand createAndExerciseBar_SomeChoice(String aName) { /* ... */ }

  public static class ContractId {
    public final String contractId;

    public ExerciseCommand exerciseArchive(Unit arg) { /* ... */ }

    public ExerciseCommand exerciseBar_SomeChoice(Bar_SomeChoice arg) { /* ... */ }

    public ExerciseCommand exerciseBar_SomeChoice(String aName) { /* ... */ }
  }

  public static class Contract {
    public final ContractId id;
    public final Bar data;

    public static Contract fromCreatedEvent(CreatedEvent event) { /* ... */ }
  }
}

Note that the static methods returning an ExerciseByKeyCommand will only be generated for templates that define a key.

Variants (a.k.a sum types)

A variant or sum type is a type with multiple constructors, where each constructor wraps a value of another type. The generated code is comprised of an abstract class for the variant type itself and a subclass thereof for each constructor. Classes for variant constructors are similar to classes for records.

Com/Acme/Variants.daml
module Com.Acme.Variants where

data BookAttribute = Pages Int
                   | Authors [Text]
                   | Title Text
                   | Published with year: Int; publisher: Text

The Java code generated for this variant is:

com/acme/variants/BookAttribute.java
package com.acme.variants;

public class BookAttribute {
  public static BookAttribute fromValue(Value value) { /* ... */ }

  public static BookAttribute fromValue(Value value) { /* ... */ }
  public Value toValue() { /* ... */ }
}
com/acme/variants/bookattribute/Pages.java
package com.acme.variants.bookattribute;

public class Pages extends BookAttribute {
  public final Long longValue;

  public static Pages fromValue(Value value) { /* ... */ }

  public Pages(Long longValue) { /* ... */ }
  public Value toValue() { /* ... */ }
}
com/acme/variants/bookattribute/Authors.java
package com.acme.variants.bookattribute;

public class Authors extends BookAttribute {
  public final List<String> listValue;

  public static Authors fromValue(Value value) { /* ... */ }

  public Author(List<String> listValue) { /* ... */ }
  public Value toValue() { /* ... */ }

}
com/acme/variants/bookattribute/Title.java
package com.acme.variants.bookattribute;

public class Title extends BookAttribute {
  public final String stringValue;

  public static Title fromValue(Value value) { /* ... */ }

  public Title(String stringValue) { /* ... */ }
  public Value toValue() { /* ... */ }
}
com/acme/variants/bookattribute/Published.java
package com.acme.variants.bookattribute;

public class Published extends BookAttribute {
  public final Long year;
  public final String publisher;

  public static Published fromValue(Value value) { /* ... */ }

  public Published(Long year, String publisher) { /* ... */ }
  public Record toValue() { /* ... */ }
}

Parameterized types

Note

This section is only included for completeness: we don’t expect users to make use of the fromValue and toValue methods, because they would typically come from a template that doesn’t have any unbound type parameters.

The Java codegen uses Java Generic types to represent DAML parameterized types.

This DAML fragment defines the parameterized type Attribute, used by the BookAttribute type for modeling the characteristics of the book:

Com/Acme/ParametrizedTypes.daml
module Com.Acme.ParameterizedTypes where

data Attribute a = Attribute
    with v : a

data BookAttributes = BookAttributes with
   pages : (Attribute Int)
   authors : (Attribute [Text])
   title : (Attribute Text)

The Java codegen generates a Java file with a generic class for the Attribute a data type:

com/acme/parametrizedtypes/Attribute.java
package com.acme.parametrizedtypes;

public class Attribute<a> {
  public final a value;

  public Attribute(a value) { /* ... */  }

  public Record toValue(Function<a, Value> toValuea) { /* ... */ }

  public static <a> Attribute<a> fromValue(Value value$, Function<Value, a> fromValuea) { /* ... */ }
}

Enums

An enum type is a simplified sum type with multiple constructors but without argument nor type parameters. The generated code is standard java Enum whose constants map enum type constructors.

Com/Acme/Enum.daml
module Com.Acme.Enum where

data Color = Red | Blue | Green

The Java code generated for this variant is:

com/acme/enum/Color.java
package com.acme.enum;


public enum Color {
  RED,

  GREEN,

  BLUE;

  /* ... */

  public static final Color fromValue(Value value$) { /* ... */ }

  public final DamlEnum toValue() {  /* ... */ }
}
com/acme/enum/bookattribute/Authors.java
package com.acme.enum.bookattribute;

public class Authors extends BookAttribute {
  public final List<String> listValue;

  public static Authors fromValue(Value value) { /* ... */ }

  public Author(List<String> listValue) { /* ... */ }
  public Value toValue() { /* ... */ }

}
Convert a value of a generated type to a Java Bindings value

To convert an instance of the generic type Attribute<a> to a Java Bindings Value, call the toValue method and pass a function as the toValuea argument for converting the field of type a to the respective Java Bindings Value. The name of the parameter consists of toValue and the name of the type parameter, in this case a, to form the name toValuea.

Below is a Java fragment that converts an attribute with a java.lang.Long value to the Java Bindings representation using the method reference Int64::new.

Attribute<Long> pagesAttribute = new Attributes<>(42L);

Value serializedPages = pagesAttribute.toValue(Int64::new);

See DAML To Java Type Mapping for an overview of the Java Bindings Value types.

Note: If the DAML type is a record or variant with more than one type parameter, you need to pass a conversion function to the toValue method for each type parameter.

Create a value of a generated type from a Java Bindings value

Analogous to the toValue method, to create a value of a generated type, call the method fromValue and pass conversion functions from a Java Bindings Value type to the expected Java type.

Attribute<Long> pagesAttribute = Attribute.<Long>fromValue(serializedPages,
    f -> f.asInt64().getOrElseThrow(() -> throw new IllegalArgumentException("Expected Int field").getValue());

See Java Bindings Value class for the methods to transform the Java Bindings types into corresponding Java types.

Non-exposed parameterized types

If the parameterized type is contained in a type where the actual type is specified (as in the BookAttributes type above), then the conversion methods of the enclosing type provides the required conversion function parameters automatically.

Convert Optional values

The conversion of the Java Optional requires two steps. The Optional must be mapped in order to convert its contains before to be passed to DamlOptional::of function.

Attribute<Optional<Long>> idAttribute = new Attribute<List<Long>>(Optional.of(42));

val serializedId = DamlOptional.of(idAttribute.map(Int64::new));

To convert back DamlOptional to Java Optional, one must use the containers method toOptional. This method expects a function to convert back the value possibiy contains in the container.

Attribute<Optional<Long>> idAttribute2 =
  serializedId.toOptional(v -> v.asInt64().orElseThrow(() -> new IllegalArgumentException("Expected Int64 element")));
Convert Collection values

DamlCollectors provides collectors to converted Java collection containers such as List and Map to DamlValues in one pass. The builders for those collectors require functions to convert the element of the container.

Attribute<List<String>> authorsAttribute =
    new Attribute<List<String>>(Arrays.asList("Homer", "Ovid", "Vergil"));

Value serializedAuthors =
    authorsAttribute.toValue(f -> f.stream().collect(DamlCollector.toList(Text::new));

To convert back DAML containers to Java ones, one must use the containers methods toList or toMap. Those methods expect functions to convert back the container’s entries.

Attribute<List<String>> authorsAttribute2 =
    Attribute.<List<String>>fromValue(
        serializedAuthors,
        f0 -> f0.asList().orElseThrow(() -> new IllegalArgumentException("Expected DamlList field"))
             .toList(
                 f1 -> f1.asText().orElseThrow(() -> new IllegalArgumentException("Expected Text element"))
                      .getValue()
             )
    );