Skip to content

Server Address Space

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

Organize application data as nodes and references with stable identities.

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

Namespace and lifecycle

Start from Your first server. Extend ManagedNamespaceWithLifecycle for a namespace whose nodes live in a UaNodeManager. Its constructor registers the namespace URI in the server's namespace table and obtains an index. Startup registers the namespace and its manager with the address space; shutdown removes those registrations. Add node construction and application resource cleanup through getLifecycleManager().

AddressSpace handles service operations. UaNodeManager stores managed nodes and references. A managed namespace combines those roles with a namespace-based filter. A custom address space may serve virtual nodes instead, but it must implement consistent routing, reads, browsing, results, and monitored-item lifecycle. Node-management service support does not follow automatically from having an in-memory manager.

Give the namespace lifecycle to server.addLifecycleParticipant(namespace) before startup, as shown in Configuration. The inherited sampling manager starts after the namespace's own lifecycle work and stops before it. Shut down device work and callbacks before releasing their state.

Stable identities and a Variable

Persist the namespace URI plus identifier, not the numeric namespace index assigned by a particular server run. Within a namespace, newNodeId(...) and newQualifiedName(...) use its current index. Choose identifiers that survive application restarts and model reorganization. Browse names locate nodes in a model, but are not a substitute for a stable NodeId.

This fragment runs in a managed namespace. context is its getNodeContext(), nodeManager is its getNodeManager(), variableId/variableName come from the namespace helpers, name is the display string, and value is a Java double. Import UaVariableNode from org.eclipse.milo.opcua.sdk.server.nodes, AccessLevel, ValueRanks, and Reference from org.eclipse.milo.opcua.sdk.core, NodeIds from org.eclipse.milo.opcua.stack.core, and DataValue, Variant, and LocalizedText from org.eclipse.milo.opcua.stack.core.types.builtin.

UaVariableNode variable =
    UaVariableNode.builder(context)
        .setNodeId(variableId)
        .setBrowseName(variableName)
        .setDisplayName(LocalizedText.english(name))
        .setDataType(NodeIds.Double)
        .setTypeDefinition(NodeIds.BaseDataVariableType)
        .setAccessLevel(AccessLevel.READ_WRITE)
        .setUserAccessLevel(AccessLevel.READ_WRITE)
        .setValueRank(ValueRanks.Scalar)
        .setMinimumSamplingInterval(0.0)
        .setValue(new DataValue(new Variant(value)))
        .build();
nodeManager.addNode(variable);
variable.addReference(
    new Reference(
        variableId,
        NodeIds.Organizes,
        NodeIds.ObjectsFolder.expanded(),
        Reference.Direction.INVERSE));

For a name of Temperature and a value of 21.5, clients can browse from Objects and read a scalar Double with Good status. The inverse Organizes reference declares placement under Objects; managed browsing gathers the counterpart. The HasTypeDefinition reference established by the builder identifies BaseDataVariableType. Adding a node to storage alone does not give it a useful browse path. Explicit event notifier hierarchies require paired references, as described in Events.

The example permits client writes for later tutorials. A read-only device must set both access attributes to READ_ONLY; applications still need an explicit policy for user-dependent access.

Objects, Methods, and types

Use Objects to group components, Variables for typed values, and Methods for operations on an owning Object. Organizes describes organization; HasComponent and HasProperty express model relationships. A Method must belong to the Object in a Call request. A type definition points to an ObjectType or VariableType, while HasSubtype relates types. A type node does not implement application behavior by itself.

Use Types and instantiation when a reusable type supplies children, mandatory members, optional members, or Methods. A general NodeSet2 importer is not supplied by this SDK. Generated standard model classes and XML value decoding do not import an arbitrary companion specification.

Results and ownership

Unknown node reads return Bad_NodeIdUnknown. Wrong data types, missing references, or a namespace that never started are separate failures; inspect operation results and browse the expected parent when diagnosing them. All public NodeIds must resolve through the address space that claims them.

The application owns node lifetime. Remove Method bindings, Condition registrations, and external callbacks before deleting their nodes. node.delete() removes the node and its owned relationships according to the node API; it is not a universal disposal method for drivers or timers. For a model created by NodeInstantiator, retain its result and use deleteCreated() to remove exactly its recorded additions. Namespace unregistration makes its nodes inaccessible; application storage and device resources still need their own cleanup.

Examples and reference

Related pages

Server SDK · Data access · Types and instantiation

Clone this wiki locally