Skip to content

Alias Names

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

Milo 1.2.0-RC1, Java 17. Source baseline 69cb25087804.

Alias Names associate stable application names with one or more target nodes, so clients can look nodes up by name instead of hard-coding NodeIds. Servers publish aliases in categories, and clients discover targets through the Part 17 FindAlias Methods. For the tutorial thermostat, an alias named Thermostat.Setpoint lets a client find the Setpoint Variable by name. This page covers both sides, the server's AliasManager and the client's FindAlias call. It assumes you know how a server builds its address space, from Server address space.

How aliases are organized

An alias is a node in the address space. It lives under a category, an Object that groups aliases, and it has AliasFor references to its targets. The standard model provides the Aliases root category with TagVariables and Topics beneath it, and applications can add their own categories. Each category has a FindAlias Method that searches it and its nested categories by name pattern and returns each matching alias with its target ExpandedNodeIds. The address space is the source of truth, not a separately maintained alias index.

The standard model contains Aliases, TagVariables, Topics, and their FindAlias Method nodes, but they do not activate alias behavior by themselves. On a Milo server, an AliasManager binds those Methods, creates and removes aliases, and tracks each category's version.

Start an AliasManager

Build a configuration with AliasManagerConfig.builder(), construct AliasManager(server, config), and start it after the server starts, because the standard nodes it binds must exist first. Shut it down before the server. If a server lifecycle participant integrates the manager, make that participant own this sequence.

This method starts a manager with the default configuration, except that it allocates NodeIds for the nodes it creates in your application namespace. The tutorial server does not start an alias manager, so this is code you would add after its startup, passing the index of urn:eclipse:milo:wiki.

static AliasManager startAliasManager(OpcUaServer server, UShort namespaceIndex) {
  AliasManagerConfig config =
      AliasManagerConfig.builder().nodeNamespaceIndex(namespaceIndex).build();

  // Call this after server.startup() completes, and shut the manager down before the server.
  var aliases = new AliasManager(server, config);
  aliases.startup();
  return aliases;
}

The default configuration permits network searches and disables the optional network mutation methods. The builder defaults are:

  • nodeNamespaceIndex=1. Choose the application namespace when generated nodes belong to your model.
  • InMemoryAliasVersionStore
  • ALLOW_FIND_DENY_MUTATE
  • AliasLimits.defaults() (1,000 results, 512 pattern characters, 1,000 operations per call)
  • AliasTarget.DEFAULT_ORDERING
  • findAliasVerboseEnabled=false
  • configurationEnabled=false

Choose authorization, limits, optional methods, and a durable version store before production startup. The sections on authorization and persistence explain each choice.

Publish an alias

This method publishes Thermostat.Setpoint in the TagVariables category, pointing at the Setpoint Variable, through a running manager. It returns the created or existing alias NodeId.

static NodeId publishSetpointAlias(AliasManager aliases, NodeId setpointId) throws UaException {
  // A null serverUri selects the local server.
  var target = new AliasTarget(setpointId.expanded(), null, NodeIds.AliasFor);

  return aliases.addAlias(NodeIds.TagVariables, "Thermostat.Setpoint", List.of(target));
}

The second AliasTarget argument is the serverUri. Use AliasFor or a subtype as the target reference type. Adding the same association again is idempotent, so repeating the call is safe.

A name can have several targets. Target order expresses preference and is configurable. The default puts local targets before remote ones, then sorts by each NodeId's parseable string form, so numeric 10 can sort before 9.

TagVariables and its descendants require Variable targets, which is why Setpoint fits there. Topics and its descendants require PublishedDataSetType instances or subtypes. The current programmatic mutation API accepts only local targets, although lookup can expose remote target references already present in the address space.

The programmatic API is trusted application code and bypasses network authorization, so keep it behind your own application boundary.

Find aliases from a client

FindAlias takes a Part 4 Like pattern and a reference-type filter. A null NodeId filter means no restriction. Searching a category also searches its nested categories, so the root Aliases category finds aliases in TagVariables too. This method calls the standard root Method and decodes its results.

static List<AliasNameDataType> findAliases(OpcUaClient client, String pattern)
    throws UaException {
  // A null reference type filter matches aliases of any reference type.
  Variant[] inputs = {new Variant(pattern), new Variant(NodeId.NULL_VALUE)};
  var request = new CallMethodRequest(NodeIds.Aliases, NodeIds.Aliases_FindAlias, inputs);

  CallResponse response = client.call(List.of(request));
  CallMethodResult result = requireNonNull(response.getResults())[0];
  if (!result.getStatusCode().isGood()) {
    throw new UaException(result.getStatusCode());
  }

  // The only output is an array of encoded AliasNameDataType structures.
  Variant[] outputs = requireNonNull(result.getOutputArguments());
  ExtensionObject[] encoded = (ExtensionObject[]) outputs[0].value();

  List<AliasNameDataType> aliases = new ArrayList<>();
  if (encoded != null) {
    for (ExtensionObject entry : encoded) {
      aliases.add((AliasNameDataType) entry.decode(client.getStaticEncodingContext()));
    }
  }
  return aliases;
}

With the alias published above, searching for Thermostat.% finds Thermostat.Setpoint, with the Setpoint node as its target. An unmatched pattern returns an empty list. The pattern uses the OPC UA Like grammar, not Java regex or shell globbing, so % matches zero or more characters. Reference-type filtering includes supported subtype relationships.

Read an alias target

Each result lists its targets as ExpandedNodeIds in the server's order of preference. Validate each target's server and namespace qualifiers before using it. This method resolves the first local target with the client's namespace table and reads its value.

static DataValue readFirstLocalTarget(OpcUaClient client, AliasNameDataType alias)
    throws UaException {
  ExpandedNodeId[] targets = requireNonNull(alias.getReferencedNodes());

  for (ExpandedNodeId target : targets) {
    // Empty for a remote target or a namespace the client's table does not contain.
    Optional<NodeId> localId = target.toNodeId(client.getNamespaceTable());
    if (localId.isPresent()) {
      return client.readValue(0.0, TimestampsToReturn.Both, localId.get());
    }
  }

  String aliasName = alias.getAliasName().name();
  throw new UaException(StatusCodes.Bad_NotFound, "No local target for " + aliasName);
}

For Thermostat.Setpoint on a server that publishes it, the read returns Good and the current setpoint. A remote target requires the correct connection to that server. Alias names do not grant permission to read or write their targets, so check the read status as you would for any Read.

Organize categories

Use the standard categories, or add application categories with AliasCategoryConfig and addCategory. Category configuration selects its NodeId, BrowseName, parent category, owning NodeManager, and alias NodeId allocation. adoptCategory attaches behavior to an existing compatible category. Keep node ownership explicit so shutdown and removal do not surprise other address-space components.

Remove aliases

Call deleteAlias(categoryId, name, targets) to remove selected target references. Passing null removes the alias from that category. The alias object is deleted only if no other category organizes it. Removing its last target reference deletes the alias from every organizing category, because an alias without targets is invalid.

Identify the category with FindAliasVerbose

When FindAliasVerbose is enabled and materialized, its results also identify the category of each alias. findAliasVerboseEnabled(true) on the builder materializes it on the standard categories. It is optional, so discover its Method NodeId instead of hard-coding it, because the NodeId an example server allocates is not portable.

Authorize searches and mutations

General server Method access control runs first. AliasAuthorizationPolicy then controls network find and mutation calls. By default, search results are not filtered through Browse authorization, so a user denied Browse may still learn alias and target identities unless the alias policy applies the intended visibility rule.

checkFind is called for the entry category, not again for each nested category. Use includeResult for per-alias filtering, which applies regardless of which ancestor the client searches. Programmatic findAlias and findAliasVerbose, like programmatic mutation, bypass this network policy.

To allow network mutation, enable the mutation Methods and explicitly grant calls through checkMutate. Both are required, and exposing the nodes alone does not authorize writes. Network mutation validates the call-wide shape before processing entries, then returns per-entry ErrorCodes for individual failures. Entries may partially succeed, so inspect those codes as well as the Call result. Programmatic mutation instead fails fast.

Persist versions and definitions

Clients can cache results against a category's LastChange, so the root version must remain monotonic across restarts. Supply an AliasVersionStore that durably persists namespace-URI-qualified category IDs and VersionTime values. Ancestor categories are updated when descendants change.

A store-load failure fails startup. A failed version save aborts the affected mutation before the new value or content becomes observable.

The version store persists versions only. Persist alias and category definitions, stable NodeIds, target references, and any policy metadata separately, and reconstruct them coherently at startup. Retain high-water versions if category NodeIds may be reused across restarts. The default memory store loses them when the process exits.

Out-of-band address-space edits do not update LastChange. Use the manager's mutation methods, or call touch explicitly where appropriate. Preserve both directions of the relationships used for ancestor and category traversal. Concurrent search and mutation have weak, browse-like consistency rather than transactional snapshots.

When things go wrong

The mutation methods report these failures:

Status Cause
Bad_InvalidArgument Empty names or targets, non-AliasFor reference types, and category or target NodeClass violations. deleteAlias also returns it for a non-null empty target list.
Bad_NodeIdUnknown A missing category or target
Bad_NotSupported A remote mutation target
Bad_NodeIdExists A collision
Bad_InternalError Can mean version persistence failed
Bad_NotFound deleteAlias received an unknown name

A stopped manager throws IllegalStateException.

Network calls fail with these statuses:

Status Cause What to do
Bad_InvalidArgument from FindAlias The pattern exceeds the length limit. With the default of 512 characters, a 513-character pattern fails, and the find helper throws UaException with this status. Shorten the pattern
Bad_ResponseTooLarge Too many matches for the result limit Narrow the pattern. A limit error is not a complete result set, and FindAlias has no Browse-style continuation cursor to drain.
Bad_UserAccessDenied The alias authorization policy denied the call Correct the user's rights or the policy
Bad_TooManyOperations A mutation call exceeds the operation limit Split the call

Bound result count and pattern size with AliasLimits.

Lifecycle and cleanup

Manager startup rolls back its work on failure. Shutdown unbinds the manager's handlers and removes its own materialized methods and fragment. Aliases and categories held by application NodeManagers remain under that application's ownership, while manager-hosted nodes leave the address space with the manager. Coordinate node deletion with the owner, and persist the corresponding definition changes. Clients create no long-lived alias-search resource beyond their ordinary connection.

Examples and reference

Next steps

That completes the feature guides. When you need an exact rule or value, such as a data type mapping, an encoding detail, or a configuration default, continue with the Reference section.

Related: Server address space · Client browsing · Server access control · Client methods

Clone this wiki locally