Skip to content

Commit

Permalink
two-phase create-and-exercise with unified exercise methods in Java (#…
Browse files Browse the repository at this point in the history
…14037)

* new getCompanion methods generated for contract IDs
* document new output for exercise methods
* link to #14039

CHANGELOG_BEGIN
- [Java codegen] ``createAndExercise*`` and ``exerciseByKey*`` methods
  are deprecated; instead, use the new ``createAnd().exercise*`` and
  ``byKey(key).exercise*`` methods.

  When the ledger API supports it, interface choices will be reachable
  via ``createAnd().toInterface(Ifc.INTERFACE).exercise*`` and
  ``byKey(key).toInterface(Ifc.INTERFACE).exercise*``, exactly the
  syntax supported by contract-ID exercise; see #14056.
CHANGELOG_END
  • Loading branch information
S11001001 authored Jun 2, 2022
1 parent 1dbebba commit 4f7eefc
Show file tree
Hide file tree
Showing 17 changed files with 618 additions and 213 deletions.
85 changes: 47 additions & 38 deletions docs/source/app-dev/bindings-java/codegen.rst
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,18 @@ The Java codegen generates three classes for a Daml template:
:end-before: -- end snippet: template example
:caption: Com/Acme/Templates.daml

A file is generated that defines three Java classes:
A file is generated that defines five Java classes and an interface:

#. ``Bar``
#. ``Bar.ContractId``
#. ``Bar.Contract``
#. ``Bar.CreateAnd``
#. ``Bar.ByKey``
#. ``Bar.Exercises``

.. code-block:: java
:caption: com/acme/templates/Bar.java
:emphasize-lines: 3,22,33
:emphasize-lines: 3,18,24,32,40,44
package com.acme.templates;
Expand All @@ -186,23 +189,22 @@ A file is generated that defines three Java classes:
public final String owner;
public final String name;
public static ExerciseByKeyCommand exerciseByKeyBar_SomeChoice(BarKey key, Bar_SomeChoice arg) { /* ... */ }
public CreateAnd createAnd() { /* ... */ }
public static ExerciseByKeyCommand exerciseByKeyBar_SomeChoice(BarKey key, String aName) { /* ... */ }
public static ByKey byKey(BarKey key) { /* ... */ }
public CreateAndExerciseCommand createAndExerciseBar_SomeChoice(Bar_SomeChoice arg) { /* ... */ }
public CreateAndExerciseCommand createAndExerciseBar_SomeChoice(String aName) { /* ... */ }
public static class ContractId extends com.daml.ledger.javaapi.data.codegen.ContractId<Bar> {
public static class ContractId extends com.daml.ledger.javaapi.data.codegen.ContractId<Bar>
implements Exercises<ExerciseCommand> {
// inherited:
public final String contractId;
}
public ExerciseCommand exerciseArchive(Unit arg) { /* ... */ }
public interface Exercises<Cmd> extends com.daml.ledger.javaapi.data.codegen.Exercises<Cmd> {
default Cmd exerciseArchive(Unit arg) { /* ... */ }
public ExerciseCommand exerciseBar_SomeChoice(Bar_SomeChoice arg) { /* ... */ }
default Cmd exerciseBar_SomeChoice(Bar_SomeChoice arg) { /* ... */ }
public ExerciseCommand exerciseBar_SomeChoice(String aName) { /* ... */ }
default Cmd exerciseBar_SomeChoice(String aName) { /* ... */ }
}
public static class Contract extends ContractWithKey<ContractId, Bar, BarKey> {
Expand All @@ -212,9 +214,17 @@ A file is generated that defines three Java classes:
public static Contract fromCreatedEvent(CreatedEvent event) { /* ... */ }
}
public static final class CreateAnd
extends com.daml.ledger.javaapi.data.codegen.CreateAnd
implements Exercises<CreateAndExerciseCommand> { /* ... */ }
public static final class ByKey
extends com.daml.ledger.javaapi.data.codegen.ByKey
implements Exercises<ExerciseByKeyCommand> { /* ... */ }
}
Note that the static methods returning an ``ExerciseByKeyCommand`` will only be generated for templates that define a key.
Note that ``byKey`` and ``ByKey`` will only be generated for templates that define a key.

Variants (a.k.a Sum Types)
^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -507,61 +517,60 @@ Effectively it is a class that contains only the inner type ContractId because o
public static final INTERFACE INTERFACE = new INTERFACE();
public static final class ContractId extends com.daml.ledger.javaapi.data.codegen.ContractId<TIf> {
public static final class ContractId extends com.daml.ledger.javaapi.data.codegen.ContractId<TIf>
implements Exercises<ExerciseCommand> {
public ContractId(String contractId) { /* ... */ }
}
public ExerciseCommand exerciseUseless(Useless arg) { /* ... */ }
public interface Exercises<Cmd> extends com.daml.ledger.javaapi.data.codegen.Exercises<Cmd> {
default Cmd exerciseUseless(Useless arg) { /* ... */ }
public ExerciseCommand exerciseHam(Ham arg) { /* ... */ }
default Cmd exerciseHam(Ham arg) { /* ... */ }
}
public static final class CreateAnd
extends com.daml.ledger.javaapi.data.codegen.CreateAnd.ToInterface
implements Exercises<CreateAndExerciseCommand> { /* ... */ }
public static final class ByKey
extends com.daml.ledger.javaapi.data.codegen.ByKey.ToInterface
implements Exercises<ExerciseByKeyCommand> { /* ... */ }
public static final class INTERFACE extends InterfaceCompanion<TIf> { /* ... */}
}
For templates the code generation will be slightly different if a template implements interfaces.
Main difference here is that the choices from inherited interfaces are included in the class declaration.
Moreover to allow converting the ContractId of a template to an interface ContractId, an additional conversion method called `toInterface` is generated.
To allow converting the ContractId of a template to an interface ContractId, an additional conversion method called `toInterface` is generated.
An ``unsafeFromInterface`` is also generated to make the [unchecked] conversion in the other direction.
.. code-block:: java
:caption: interfaces/Child.java
package interfaces
/* ... */
public final class Child extends Template {
/* ... */
public CreateAndExerciseCommand createAndExerciseHam(Ham arg) { /* ... */ }
public CreateAndExerciseCommand createAndExerciseHam() { /* ... */ }
public CreateAndExerciseCommand createAndExerciseUseless(Useless arg) { /* ... */ }
public CreateAndExerciseCommand createAndExerciseUseless(TIf.ContractId interfacely) { /* ... */ }
/* ... */
public static final class ContractId extends com.daml.ledger.javaapi.data.codegen.ContractId<Child> {
public static final class ContractId extends com.daml.ledger.javaapi.data.codegen.ContractId<Child>
implements Exercises<ExerciseCommand> {
/* ... */
public ExerciseCommand exerciseHam(Ham arg) { /* ... */ }
public ExerciseCommand exerciseUseless(Useless arg) { /* ... */ }
public ExerciseCommand exerciseHam() { /* ... */ }
public ExerciseCommand exerciseUseless(TIf.ContractId interfacely) { /* ... */ }
public TIf.ContractId toInterface(TIf.INTERFACE interfaceCompanion) { /* ... */ }
public static ContractId unsafeFromInterface(TIf.ContractId interfaceContractId) { /* ... */ }
}
public interface Exercises<Cmd> extends com.daml.ledger.javaapi.data.codegen.Exercises<Cmd> {
default Cmd exerciseBar(Bar arg) { /* ... */ }
default Cmd exerciseBar() { /* ... */ }
}
/* ... */
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@

package com.daml.ledger.javaapi.data;

import com.daml.ledger.javaapi.data.codegen.CreateAnd;

public abstract class Template {

public abstract CreateCommand create();

public abstract DamlRecord toValue();

/**
* Set up a {@link CreateAndExerciseCommand}; invoke an {@code exercise} method on the result of
* this to finish creating the command, or convert to an interface first with {@code toInterface}
* to invoke an interface {@code exercise} method.
*/
public abstract CreateAnd createAnd();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.ExerciseByKeyCommand;
import com.daml.ledger.javaapi.data.Value;

/** Parent of all generated {@code ByKey} classes within templates and interfaces. */
public abstract class ByKey implements Exercises<ExerciseByKeyCommand> {
protected final Value contractKey;

protected ByKey(Value contractKey) {
this.contractKey = contractKey;
}

@Override
public ExerciseByKeyCommand makeExerciseCmd(String choice, Value choiceArgument) {
return new ExerciseByKeyCommand(
getCompanion().TEMPLATE_ID, contractKey, choice, choiceArgument);
}

/** The origin of the choice, not the template relevant to contractKey. */
protected abstract ContractTypeCompanion getCompanion();

/**
* Parent of all generated {@code ByKey} classes within interfaces. These need to pass both the
* template and interface ID.
*/
public abstract static class ToInterface extends ByKey {
private final ContractCompanion<?, ?, ?> keySource;

protected ToInterface(ContractCompanion<?, ?, ?> keySource, Value contractKey) {
super(contractKey);
this.keySource = keySource;
}

@Override
public ExerciseByKeyCommand makeExerciseCmd(String choice, Value choiceArgument) {
// TODO #14056 use getCompanion().TEMPLATE_ID as the interface ID
return new ExerciseByKeyCommand(keySource.TEMPLATE_ID, contractKey, choice, choiceArgument);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@
* @param <Data> The generated {@link com.daml.ledger.javaapi.data.Template} subclass named after
* the template, whose instances contain only the payload.
*/
public abstract class ContractCompanion<Ct, Id, Data> {
/** The full template ID of the template that defined this companion. */
public final Identifier TEMPLATE_ID;

public abstract class ContractCompanion<Ct, Id, Data> extends ContractTypeCompanion {
final String templateClassName; // not something we want outside this package

protected final Function<String, Id> newContractId;
Expand All @@ -45,7 +42,7 @@ protected ContractCompanion(
Identifier templateId,
Function<String, Id> newContractId,
Function<DamlRecord, Data> fromValue) {
this.TEMPLATE_ID = templateId;
super(templateId);
this.templateClassName = templateClassName;
this.newContractId = newContractId;
this.fromValue = fromValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.ExerciseCommand;
import com.daml.ledger.javaapi.data.Value;
import java.util.Objects;

Expand Down Expand Up @@ -35,7 +36,7 @@
*
* @param <T> A template type
*/
public class ContractId<T> {
public class ContractId<T> implements Exercises<ExerciseCommand> {
public final String contractId;

public ContractId(String contractId) {
Expand All @@ -46,6 +47,18 @@ public final Value toValue() {
return new com.daml.ledger.javaapi.data.ContractId(contractId);
}

@Override
public final ExerciseCommand makeExerciseCmd(String choice, Value choiceArgument) {
return new ExerciseCommand(getCompanion().TEMPLATE_ID, contractId, choice, choiceArgument);
}

// overridden by every code generator, but decoding abstractly can e.g.
// produce a ContractId<Foo> that is not a Foo.ContractId
protected ContractTypeCompanion getCompanion() {
throw new UnsupportedOperationException(
"Cannot exercise on a contract ID type without code-generated exercise methods");
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.Identifier;

/** The commonality between {@link ContractCompanion} and {@link InterfaceCompanion}. */
public abstract class ContractTypeCompanion {
/** The full template ID of the template or interface that defined this companion. */
public final Identifier TEMPLATE_ID;

protected ContractTypeCompanion(Identifier templateId) {
TEMPLATE_ID = templateId;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.CreateAndExerciseCommand;
import com.daml.ledger.javaapi.data.Template;
import com.daml.ledger.javaapi.data.Value;

/** Parent of all generated {@code CreateAnd} classes within templates and interfaces. */
public abstract class CreateAnd implements Exercises<CreateAndExerciseCommand> {
protected final Template createArguments;

protected CreateAnd(Template createArguments) {
this.createArguments = createArguments;
}

@Override
public CreateAndExerciseCommand makeExerciseCmd(String choice, Value choiceArgument) {
return new CreateAndExerciseCommand(
getCompanion().TEMPLATE_ID, createArguments.toValue(), choice, choiceArgument);
}

/** The origin of the choice, not the createArguments. */
protected abstract ContractTypeCompanion getCompanion();

/**
* Parent of all generated {@code CreateAnd} classes within interfaces. These need to pass both
* the template and interface ID.
*/
public abstract static class ToInterface extends CreateAnd {
private final ContractCompanion<?, ?, ?> createSource;

protected ToInterface(ContractCompanion<?, ?, ?> createSource, Template createArguments) {
super(createArguments);
this.createSource = createSource;
}

@Override
public final CreateAndExerciseCommand makeExerciseCmd(String choice, Value choiceArgument) {
// TODO #14056 use getCompanion().TEMPLATE_ID as the interface ID
return new CreateAndExerciseCommand(
createSource.TEMPLATE_ID, createArguments.toValue(), choice, choiceArgument);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.Value;

/**
* Root of all generated {@code Exercises} interfaces for templates and Daml interfaces.
*
* @param <Cmd> The returned type of ledger command.
*/
public interface Exercises<Cmd> {
Cmd makeExerciseCmd(String choice, Value choiceArgument);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@

package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.Identifier;

/**
* Metadata and utilities associated with an interface as a whole. Its subclasses serve to
* disambiguate various generated {@code toInterface} overloads.
*
* @param <I> The generated interface marker class.
*/
public abstract class InterfaceCompanion<I> {
protected InterfaceCompanion() {}
public abstract class InterfaceCompanion<I> extends ContractTypeCompanion {
protected InterfaceCompanion(Identifier templateId) {
super(templateId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ template Child
party: Party
where
signatory party
key party: Party
maintainer key

choice Bar: () with
controller party
do
Expand Down
Loading

0 comments on commit 4f7eefc

Please sign in to comment.