Class AttributeMap<T>
- Type Parameters:
T- the Java value type stored in every map instance
An AttributeMap is useful when keys are dynamically determined at runtime. Register
the definition once in the Flow's PersistenceSchema, then supply an instance name for
each read, write, delete, or lock. Each key/value pair is stored as a separate blob, so updating
one entry does not rewrite a large Attribute containing the entire logical map. Each value is
serialized with the definition's concrete Class. Instance names must be nonblank and
must not contain /.
private final AttributeMap<String> orderStatus =
AttributeMap.define("order-status", String.class);
orderStatus.set(context, orderId, "PAID");
String status = orderStatus.get(context, orderId);
-
Method Summary
Modifier and TypeMethodDescriptionstatic <T> AttributeMap<T> Defines an Attribute map without a search index.static <T> AttributeMap<T> define(String name, Class<T> valueType, AttributeIndex index) Defines an Attribute map with a search index.voidDeletes one map instance in the current invocation.Reads one map instance from the current invocation.getAllInstanceKeys(Context context) Returns existing instance keys in ascending order, including buffered writes.intgetMapSize(Context context) Returns the number of existing instances, including writes buffered by this invocation.Returns a definition whose instance values are stored only in the Dex blob store and are permanently deleted when an instance is deleted or set tonull.Selects one typed instance of this AttributeMap for a Client Attribute read.voidWrites one map instance in the current invocation.Returns a definition whose instance writes are projected through the Flow's Attribute Store.
-
Method Details
-
define
Defines an Attribute map without a search index.- Type Parameters:
T- the Java value type- Parameters:
name- the stable Attribute-map name; must not be blank or contain/valueType- the concrete Java class used for every instance value- Returns:
- the typed Attribute-map definition
- Throws:
IllegalArgumentException- ifnameisnull, blank, or contains/NullPointerException- ifvalueTypeisnull
-
define
Defines an Attribute map with a search index.- Type Parameters:
T- the Java value type- Parameters:
name- the stable Attribute-map name; must not be blank or contain/valueType- the concrete Java class used for every instance valueindex- the search-index definition, ornullfor no index- Returns:
- the typed Attribute-map definition
- Throws:
IllegalArgumentException- ifnameisnull, blank, or contains/NullPointerException- ifvalueTypeisnull
-
syncToAttributeStore
Returns a definition whose instance writes are projected through the Flow's Attribute Store.The physical Attribute-map key must match a target column. Projection is asynchronous and does not roll back the Flow Attribute when external storage fails. Deletion projects SQL
NULLwhen the target column permits it.- Returns:
- a new immutable Attribute-map definition with Attribute Store synchronization enabled
- Throws:
IllegalStateException- if this definition useshardDeleteOnNull()
-
hardDeleteOnNull
Returns a definition whose instance values are stored only in the Dex blob store and are permanently deleted when an instance is deleted or set tonull.Use this option for short-lived secrets that are keyed at runtime, such as one API key per tenant or per connected account. Each instance value is never recorded in Flow history. Dex stores it only in the blob store and records only a reference in Flow history. Calling
delete(Context, String)orset(Context, String, Object)withnullpermanently deletes that instance's stored value. After the deletion, the instance reads as absent. Other instances are unaffected.Only string and object values are supported. Object values include JSON-mapped classes and
byte[]. Numeric and Boolean value types, such asInteger,Long,Double, andBoolean, are rejected when this option is applied. When the declared value type is broader, such asObjectorNumber, a write whose value maps to a number or a Boolean is rejected when it is written. Writingnullis always allowed.This option cannot be combined with an
AttributeIndexor withsyncToAttributeStore().Client.waitForAttributeMatch(String, Attribute, AttributeMatch, WaitForAttributeOptions)rejects a hard-delete Attribute map before it contacts the Server. Use an RPC that reads the instance when a caller needs the value.Client.getAttributes(String, AttributeRead...)also reads a hard-delete instance's committed value.The Dex Server must have an enabled blob store and must support Server protocol version 3 or later. A Step, RPC, StartFlow, or SubFlow start that writes this Attribute map fails with
IllegalStateExceptionwhen the negotiated Server protocol is older. The Server rejects the write when its blob store is disabled. Steps and RPC handlers receive instance values inline. The SDK never stores a hard-delete value in the localBlobCache.Know these limitations before you store a secret:
- Each Flow keeps one stored copy per instance key. Every write overwrites that copy in place.
- A Step or RPC attempt writes or deletes the stored value before Dex commits the attempt's result. A retried attempt, other Steps, and other readers can therefore observe a write or a deletion from an attempt that later failed.
- When concurrent Steps or RPCs write the same instance, the final stored value may not be
the value from the last committed write. When the order matters, use a single writer,
or make every Step and RPC that writes the instance hold an
AttributeLockon it. A failed locked attempt can still overwrite the value. - A
Client.startFlow(Flow, String, I)call orSubFlow.run(Class, I)start that fails because the Flow ID is already running still overwrites the running Flow's instance value. With anullinitial value, it deletes the running Flow's instance value. - Executions that reuse a Flow ID share the stored values.
Client.timeTravel(String, TimeTravelOptions)does not restore a deleted value. A deleted value reads as absent.- A Flow retry or a restarted SubFlow starts from the original initial value. If an earlier run deleted that value, the new run reads it as absent.
- Removing
hardDeleteOnNull()from a definition leaves the stored value in place until Dex removes the Flow. Delete the value with the hard-delete definition first. - A value remains in the blob store until it is deleted, or until Flow retention removes the Flow and blob cleanup runs.
- Blob store backups and bucket versioning can retain copies after deletion. Configure them separately when deleted secrets must not survive.
final class TenantSyncFlow implements Flow<String> { final AttributeMap<String> tenantApiKeys = AttributeMap.define("tenant-api-keys", String.class).hardDeleteOnNull(); private final SyncTenant sync = new SyncTenant(tenantApiKeys); @Override public StepList<String> getSteps() { return StepList.startStep(sync); } @Override public PersistenceSchema getPersistenceSchema() { return PersistenceSchema.of(tenantApiKeys); } } final class SyncTenant implements Step<String> { private final AttributeMap<String> tenantApiKeys; SyncTenant(final AttributeMap<String> tenantApiKeys) { this.tenantApiKeys = tenantApiKeys; } @Override public StepDecision execute(final Context context, final String tenantId) { final String key = tenantApiKeys.get(context, tenantId); ProviderApi.sync(tenantId, key); // Permanently delete this tenant's secret once it is no longer needed. tenantApiKeys.delete(context, tenantId); return StepDecision.gracefulComplete(); } } // Start the Flow with one tenant's secret as an initial instance value. client.startFlow( flow, "tenant-sync-7", "tenant-7", StartFlowOptions.newBuilder() .addAttribute(flow.tenantApiKeys, "tenant-7", System.getenv("TENANT_7_KEY")) .build());- Returns:
- a new immutable Attribute-map definition with hard deletion on
nullenabled - Throws:
IllegalStateException- if this definition has an index, usessyncToAttributeStore(), or declares a numeric or Boolean value type
-
instance
Selects one typed instance of this AttributeMap for a Client Attribute read.Pass the result to
Client.getAttributes(String, AttributeRead...), and pass the same selector, or an equal one, toAttributeValues.get(AttributeRead)to obtain the value. The selector keeps this map's value type, sogetreturnsOptional<T>. Selecting an instance does not contact the Server and does not check that the instance exists.This method selects an instance that a Client reads from outside the Flow. It differs from
get(Context, String), which reads an instance inside a Step or RPC handler.AttributeMap<Note> notes = orderFlow.notes; AttributeValues values = client.getAttributes("order-42", notes.instance("n1")); Optional<Note> firstNote = values.get(notes.instance("n1"));- Parameters:
instance- the non-blank logical instance key; it must not contain/- Returns:
- an immutable selector for this AttributeMap object and
instance - Throws:
IllegalArgumentException- ifinstanceisnull, blank, or contains/
-
get
-
set
Writes one map instance in the current invocation.- Parameters:
context- the Step or RPC invocation contextinstance- the instance key within this mapvalue- the typed value to persist;nulldeletes the instance- Throws:
IllegalArgumentException- if this is ahardDeleteOnNull()Attribute map and the value maps to a number or a Boolean
-
delete
-
getMapSize
Returns the number of existing instances, including writes buffered by this invocation.- Parameters:
context- the Step or RPC invocation context- Returns:
- the current number of instances
-
getAllInstanceKeys
-