Skip to content

Client Nodes

Kevin Herron edited this page Oct 5, 2026 · 10 revisions

AddressSpace creates client-side node wrappers and generated model types. A wrapper represents a remote node; constructing one does not create a node on the server.

Milo 1.2.0-SNAPSHOT, Java 17. Source baseline 7faaf4f0516c.

Find and create wrappers

Use client.getAddressSpace().getNode(nodeId) when the NodeClass is not known. Use getObjectNode(...) or getVariableNode(...) when it is. The helpers read required attributes and type information, consult the node cache and type managers, and choose a wrapper. A cached lookup returns the same wrapper instance to other callers. By default, ordinary cache entries expire two minutes after being written and are bounded to 16,384 entries; canonicalized entries have separate retention. A wrong class or missing node fails the lookup. Overloads accepting a type-definition NodeId are useful when that information is already known.

Standard model wrappers, such as ServerTypeNode, give named access to model members and properties. getServerNode() is a convenient entry point. A custom generated type must be registered with the appropriate client type manager before expecting its specialized wrapper. A Java cast does not discover a type definition or register a model.

Properties are referenced Variable nodes, not Java fields on the remote object. Generated property helpers can locate and read those nodes; their exact return types and remote behavior come from the generated API. Distinguish these helpers from basic cached attribute getters such as getDisplayName().

Cached attributes versus remote operations

Operation on a wrapper Effect
Basic attribute getXxx() / setXxx(...) Read/change the local cached attribute. A setter alone does not write remotely.
readXxx() / writeXxx(...) Perform a remote call; typed attribute reads and successful attribute writes also update cached attributes. Generated property writes do not refresh their property cache. readValue() caches even a Bad DataValue. Generated model-property readers return payloads and discard status; use readAttribute(...) or the property node's readValue() when quality matters.
readAttribute(...) / writeAttribute(...) Return DataValue / StatusCode for explicit status handling.
refresh(attributes) Read selected attributes and refresh eligible local cached values. Inspect the returned DataValues.
synchronize(attributes) Write selected local values to the server. Inspect every returned StatusCode.

This helper deliberately stages an Int32 value locally, writes that one attribute, and refreshes it. It mutates a shared cached wrapper, so coordinate concurrent callers before staging or synchronizing values. Its caller supplies a connected client and a writable Int32 Variable.

static void synchronizeValue(OpcUaClient client, NodeId nodeId, int value) throws UaException {
  UaVariableNode node = client.getAddressSpace().getVariableNode(nodeId);
  node.setValue(new Variant(value));
  List<StatusCode> writes = node.synchronize(EnumSet.of(AttributeId.Value));
  if (writes.size() != 1) throw new IllegalStateException("Missing write result");
  if (!writes.get(0).isGood()) throw new UaException(writes.get(0));
  List<DataValue> reads = node.refresh(EnumSet.of(AttributeId.Value));
  if (reads.size() != 1) throw new IllegalStateException("Missing read result");
  if (!reads.get(0).statusCode().isGood()) throw new UaException(reads.get(0).statusCode());
}

The fixture verifies the remote value changes only after synchronization. It checks that a later local setter alone leaves the server value unchanged.

Refresh and invalidation

At this pinned baseline, UaNode.refreshAsync(Set) reads every requested attribute but updates the local cache for only the first result. Refresh one attribute at a time when you need a refreshed wrapper, as the example does. Inspect each returned status; Bad results do not become a fresh cached value. synchronize likewise returns operation statuses, so normal return does not prove every write succeeded. This limitation is specific to the documented source revision and should be rechecked on upgrade.

node.invalidate() removes the node's cached entry so a later AddressSpace lookup can obtain a new wrapper. It does not delete the remote node and does not mutate other Java references that still point to the old wrapper. A canonicalized node stays in the cache until explicitly invalidated; use canonicalize() only when stable wrapper identity is useful and account for memory retention.

Session reconnection does not clear the node cache. Invalidate wrappers whose namespace mapping or model assumptions changed before looking them up again. Cached attributes and model wrappers are not live subscriptions. Server configuration changes, permission changes, or reconnection can invalidate application assumptions. Refresh model metadata when necessary and use Subscriptions for live values. Persist stable namespace URIs and identifiers rather than wrappers or session-specific namespace indexes.

Errors and ownership

UaVariableNode.writeValue(...) and its attribute writers reject Bad status but accept Uncertain and update the cache. Common UaNode attribute writers and most generated model-property writers reject any non-Good status with UaException. At this baseline, 23 generated multiline-signature writers still discard status, including ServerDiagnosticsTypeNode.writeServerDiagnosticsSummary(...); inspect the async writer's returned status instead. Generated property writers do not refresh the property cache. Async attribute/property writers expose the status. Individual attribute helpers and synchronization expose statuses directly, so choose the API according to how much result detail you need. A multi-attribute synchronization may partially succeed; it has no transactional rollback.

The client owns its address-space cache. The application owns any retained wrappers and decides when to invalidate or release them. Node wrappers have no separate network connection to close; shut down the parent client when its work is finished.

Examples and reference

Related pages

Client SDK · Browsing · Reading · Writing

Clone this wiki locally