Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
30 views

Prosys_OPC_UA_SDK_for_Java_Client_Tutorial

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
30 views

Prosys_OPC_UA_SDK_for_Java_Client_Tutorial

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 35

Version 5.2.

0
Client Tutorial

Tutorial
Client
-
Java
for
SDK
UA
OPC
Prosys
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2. Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
3. Sample Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
4. UaClient Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
4.1. Server Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
4.2. Reverse Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
5. Security Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5.1. Application Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5.1.1. Application Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
5.1.2. Application Instance Certificate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
5.1.3. Issuer Certificate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
5.1.4. Multiple Application Instance Certificates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5.1.5. HTTPS Certificate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5.1.6. Assigning the Application Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5.2. Security Modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5.2.1. SecurityMode for OPC UA TCP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
5.2.2. HTTPS SecurityPolicy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
5.3. User Identity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
5.4. Validating Server Certificates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
5.4.1. Validating HTTPS Certificates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
5.5. Teach Yourself the Security Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
6. Connecting and Disconnecting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
6.1. Connection Monitoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
6.1.1. ServiceException . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
6.1.2. Timeout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6.1.3. Server Status Monitoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
6.1.4. Automatic Reconnect . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7. OPC UA Server Address Space . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7.1. Browse the Address Space . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
7.2. Browsing Through the Nodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
7.3. Translate BrowsePath to NodeId(s) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
8. Read Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
9. Write Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
10. Exceptions When Operations Fail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
11. Subscriptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
11.1. Subscribe to Data Changes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
11.1.1. DataChangeListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
11.2. Subscribe to Events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
11.2.1. SelectClauses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
11.2.2. WhereClause . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
11.2.3. MonitoredEventItem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
11.2.4. Alarms and Condition Refresh. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
11.3. SubscriptionAliveListener . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
11.4. Transferring Subscriptions Between Sessions and Automatic Reconnections . . . . . . . . . . . . . . . 25
12. History Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
12.1. Reading History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
12.1.1. Reading Variable History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
12.1.2. Reading Event History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
12.2. Updating or Deleting History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
13. Calling Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
14. Registering and Unregistering Nodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
15. Using Node Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
16. Information Modeling and Code Generation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
16.1. Prosys OPC UA SDK for Java Code Generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
16.2. Using the Generated Classes in Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
16.3. Registering the Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
16.4. Using Instances of Generated Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
17. Custom DataTypes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
17.1. Simple Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
17.2. Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
17.3. Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
17.4. OptionSets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
17.5. UaDataTypeSpecification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
17.6. EncoderContext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
1. Introduction
This is the Prosys OPC UA SDK for Java tutorial for client application development. With this quick
introduction you should be able to understand the basic ideas behind the Prosys OPC UA SDK for Java.

Note that this tutorial assumes that you are already familiar with the basic concepts of OPC UA
communications, although you can get started without much prior knowledge.

For a start on OPC UA communications, we recommend the book OPC Unified Architecture by Mahnke,
Leitner and Damm (Springer-Verlag, 2009, ISBN 978-3-540-68898-3). For a full reference, you can use
the OPC UA specification.

2. Installation
See the installation instructions in the 'README.txt' file (or the brief version on the download page). The
README file also contains notes about the usage and deployment of external libraries used by the SDK.

There is also a basic starting guide with tips on Java development tools and on using the Prosys OPC UA
SDK for Java with the Eclipse IDE located in the 'Prosys_OPC_UA_SDK_for_Java_Starting_Guide' next to
this tutorial in the distribution package.

3. Sample Applications
The SDK contains a sample client application in the SampleConsoleClient Java class. This tutorial will refer
to the code in the sample application while explaining the different steps to take in order to accomplish
the main tasks of an OPC UA client.

The samples also contain a SimpleClient Java class that presents a very straightforward example of the
most simplified client application you can create with the SDK.

Additionally, we recommend checking our OPC UA Browser and OPC UA Simulation Server. The Browser
serves as a generic graphical OPC UA Client and the Simulation Server is an OPC UA Server you can test
against.

The server editions of the SDK also include a SampleConsoleServer equivalent to the SampleConsoleClient.

4. UaClient Object
The UaClient class is the main class you will be working with. It encapsulates the connection to the OPC
UA server and handles the various details of the actual OPC UA communications, thus providing you
with a simple interface to access from your applications. These are the lines in the
'SampleConsoleClient.java' file that create the UaClient object:

protected UaClient client;


...
client = new UaClient();
client.setAddress(serverAddress);

1
4.1. Server Connection
In the previous example, the serverAddress argument defines the server you are connecting to. Some
sample addresses are provided in the following table:

Address Server

opc.tcp://<hostname>:52520/OPCUA/SampleConsole Prosys OPC UA SDK for Java Sample Console Server


Server

opc.tcp://<hostname>:53530/OPCUA/SimulationServe Prosys OPC UA Simulation Server


r

opc.https://<hostname>:53443/OPCUA/SimulationSer Prosys OPC UA Simulation Server with OPC UA


ver HTTPS, if that is enabled

opc.tcp://uademo.prosysopc.com:53530/OPCUA/Simu Publicly available instance of the Prosys OPC UA


lationServer Simulation Server

opc.tcp://localhost:48010 Unified Automation OPC UA C++ Demo Server

opc.tcp://<hostname>:62541/Quickstarts/DataAccess OPC Foundation QuickStart Data Access Server


Server

<hostname> is the hostname of the computer in which the server is running. If it is running on the
same machine localhost and 127.0.0.1 also typically work in addition to the hostname.

The servers define a list of endpoints that they are listening to. The actual
hostname in the endpoint may differ from the one that you use for connection. For
Windows hostname resolution, see http://technet.microsoft.com/en-

 us/library/bb727005.aspx. If you are using the client in Linux, you cannot use
NetBIOS computer names to access Windows servers. In general, it is best to use
TCP/IP DNS names from all clients. Alternatively, you can always use the IP address
of the computer.

The first part of the address defines the transport protocol to use. opc.tcp refers to OPC UA TCP
communication which is usually the preferred protocol. An alternative transport protocol is OPC UA
HTTPS, which some servers may support as well, but in general it is not used very much as it does not
offer real benefits to the highly optimized OPC UA TCP protocol. You can use opc.https addresses to
communicate with those servers.

OPC UA HTTPS uses HTTPS as a transport channel, but still requires OPC UA
capable applications. So, it does not enable generic web clients to communicate
with OPC UA servers.
 The OPC Foundation has defined also a WebSocket transport protocol alternative
(opc.wss), but there are not many implementations of that protocol, yet, and the
SDK does not support it either.

2
4.2. Reverse Connection
OPC UA Specification 1.04 defines a new way to open OPC UA TCP connections, called Reverse
Connection. In this mode, the server application will open the connection, contrary to the normal
connection opened by the client. This can be useful in situations where the server is behind a firewall
that cannot let client connections go through to the server.

In order to enable the reverse connection, the client application will first open a TCP/IP socket in a
custom port. The Server may then open the connection to the client socket. After that the client will
create the OPC UA TCP secure channel and session to the server as usual, including all security details.

In UaClient this can be achieved by defining the socket to be listened with either UaClient.setReversePort()
or UaClient.setReverseAddress(). For example:

client.setReverseAddress(UaAddress.parse("opc.tcp://localhost:6000"));

Now, when you call connect(), the client will go waiting for connections from servers. Note that you
should not use UaClient.setAddress() in this case.

You should also define a listener to validate incoming connections from the server using
UaClient.setReverseConnectionListener(). For example:

public class MyReverseConnectionListener implements ReverseConnectionListener {

@Override
public boolean onConnect(String serverApplicationUri, String endpointUrl, SocketAddress remoteAddress) {
/*
* You can perform here validation for reverse connections. Return false, if you wish to stop
* the connection. Note that the connection is already initiated by the server at the
* SocketAddress.
*
* NOTE! The endpointUrl parameter is sent by the Server and used in the Client in calls forming
* the higher level communication channel. It is not the actual address the client is connecting
* (as the socket is already open to the remoteAddress) and most of the time is one of the
* endpointUrls used in normal non-reverse connections for the Server.
*/
System.out.println("Accepting reverse connection to server: " + serverApplicationUri + " at: " +
remoteAddress
+ " , using endpointUrl: " + endpointUrl);
return true;
}

3
At this point in the communication the channel is not encrypted. Therefore when
possible you should validate that the server is within the list of known servers by
checking the remote SocketAddress of the server. The server ApplicationURI and
 EndpointUrl are given by the server when they connect. You can use all the security
options as in usual connections to validate that the client is communicating with a
trusted server.

5. Security Settings
OPC UA applications enable full security that is integrated into the communications. You can decide in
the client, which kind of security settings you want to use or make available in your application. Usually,
the end user should be able to configure the security level of each connection according to his needs.

5.1. Application Identity


All OPC UA applications must define some characteristics of themselves. This information is
communicated to other applications via the OPC UA protocol when the applications are connected.

For secure communications, the applications must also define an Application Instance Certificate, which
they use to authenticate themselves to other applications they are communicating with. Depending on
the selected security level, servers may only accept connections from clients that they trust.

5.1.1. Application Description


The characteristics of an OPC UA application is defined in the following method:

protected ApplicationDescription initApplicationDescription(String applicationName, ApplicationType


applicationType) {
ApplicationDescription applicationDescription = new ApplicationDescription();
// 'localhost' in the ApplicationName and ApplicationURI is converted to the actual host name
// of the computer in which the application is run.
applicationDescription.setApplicationName(new LocalizedText(applicationName + "@localhost"));
// ApplicationUri defines a unique identifier for each application instance-. Therefore, we
// use the actual computer name to ensure that it gets assigned differently in every
// installation.
applicationDescription.setApplicationUri("urn:localhost:OPCUA:" + applicationName);
// ProductUri should refer to your own company, since it identifies your product
applicationDescription.setProductUri("urn:prosysopc.com:OPCUA:" + applicationName);
applicationDescription.setApplicationType(applicationType);
return applicationDescription;
}

which can then be called, with

4
[...]
ApplicationDescription applicationDescription = initApplicationDescription(APP_NAME, ApplicationType
.Client);

ApplicationName is used in user interfaces as a name for each application instance.

ApplicationUri is a unique identifier for each running instance.

ProductUri, on the other hand, is used to identify your product and should therefore be the same for
all instances. It should refer to your own domain, for example, to ensure that it is globally unique.

Since the identifiers should be unique for each instance (i.e. installation), it is a good habit to include
the hostname of the computer in which the application is running in both the ApplicationName and the
ApplicationUri. The SDK supports this by automatically converting localhost to the actual hostname of
the computer (e.g. 'myhost'). Alternatively, you can use hostname, which will be replaced with the full
hostname, including the possible domain name part (e.g. 'myhost.mydomain.com').

The URIs must be valid identifiers, i.e. they must begin with a scheme, such as ‘urn:’
and may not contain any space characters. There are some applications in the
 market, which use invalid URIs and may therefore cause some errors or warnings
with your application.

5.1.2. Application Instance Certificate


You can define the Application Instance Certificate for the client by setting an ApplicationIdentity for the
UaClient object. The simplest way to do this is to use the loadOrCreateCertificate() method:

final ApplicationIdentity identity = ApplicationIdentity.loadOrCreateCertificate(


appDescription,
"Sample Organisation",
privateKeyPassword,
privatePath,
issuerCertificate,
keySizes,
/* Enable renewing the certificate */true);

On the first run, it creates the certificate and the private key and stores them in the folder defined by
privatePath.

privateKeyPassword may help protecting the key from misuse, but we leave it null by default.

issuerCertificate is also null by default, in which case we are creating a self-signed certificate.

keySizes is used to define the strength of the security keys. 2048 is the default in the sample and usually
good enough.

The last parameter enables automatic certificate renewal when the certificate expires.

5
As the name refers, the certificate is used to identify each application instance.
That means that on every computer, the application has a different certificate. The
certificate contains the ApplicationUri, which also identifies the computer in which
 the application is run, and must match the one defined in the
ApplicationDescription. Therefore, we provide the appDescription as a parameter for
loadOrCreateCertificate(), which extracts the ApplicationUri from it.

ApplicationIdentity can also be created with it’s constructors. You will need to load
the certificate and private key separately and also set the ApplicationDescription to
the ApplicationIdentity.
 If your application does not use security, you may also create the
ApplicationIdentity without any certificate by using the default constructor.
However, you should always define the ApplicationDescription.

Note that if some other application gets the same key pair, it can pretend to be the
same client application. The private key should be kept safe in order to reliably
verify the identity of this application. Additionally, you may secure the usage of the

 private key with a password that is required to open it for use (but you need to add
that in clear text in your application code or prompt it from the user). The
certificate is public and can be distributed and stored freely in the servers and
anywhere else.

The SDK stores the certificate into a file named


'<ApplicationName>@<hostname>_<keysize>.der' and private key in a respective
'.pem' file. If you get them from an external CA, you can just replace the files in the
file system. Sometimes the private key can be provided in a '.pfx' (PKCS#12) file.


The SDK can also use that if '.pem' is not present.

'.pfx' file may sometimes include both the private key and the certificate, but the
'loadOrCreateCertificate' method does not support reading the certificate from it.

OpenSSL can be useful for converting between different file formats - or extracting
the certificate and private key from a '.pfx' file.

5.1.3. Issuer Certificate


Instead of using self-signed certificates, it would be better to use certificates signed by a recognized
Certificate Authority (CA). Often, this should be a CA managed by an administrator in the company that
is using the applications. The idea is to define a trust between the applications and the CA helps
centralizing the management of this trust.

The CA should be run securely, and the private key of the CA should never be exposed outside the CA
computer.

For the purpose of this tutorial, we can however, create a sample CA certificate and use that for signing
our Application Instance Certificate. In order to create a sample issuer certificate, you can use, for
example:

6
KeyPair issuerCertificate =
ApplicationIdentity.loadOrCreateIssuerCertificate(
"ProsysSampleCA", privatePath, privateKeyPassword, 3650, false);

You can then use this in the loadOrCreateCertificate call.

The self-made issuer key does not replace a real CA. In real installations, it is
always best to establish a central CA and create all keys for the applications using

 the CA. In this scenario, you can copy the certificate of the CA to the trust list of
each OPC UA application. This will enable the applications to automatically trust all
keys created by the CA.

5.1.4. Multiple Application Instance Certificates


OPC UA specification defines different security profiles, which may require different kind of Application
Instance Certificates, for example with different key sizes. The SDK enables usage of several certificates
by defining an array of keySizes, e.g.:

// Use 0 to use the default keySize and default file names (for other
// values the file names will include the key size.
int[] keySizes = new int[] { 2048, 4096 };

5.1.5. HTTPS Certificate


Starting from SDK version 4.0.0 HTTPS Certificate is no longer needed or created

 on the client side. See Prosys_OPC_UA_SDK_for_Java_3_To_4_Migration_Guide and


4.0.0 Release Notes for more details.

5.1.6. Assigning the Application Identity


Now, we can finally just assign the created identity to the UaClient object:

client.setApplicationIdentity(identity);

5.2. Security Modes

5.2.1. SecurityMode for OPC UA TCP


Once the certificates are defined, you may decide the level of security that is used in the OPC UA TCP
binary communications by setting the SecurityMode:

7
client.setSecurityMode(SecurityMode.NONE);

As implied by the name, this setting enables access without any security features. Whereas it is usually
good to start practicing the communication without security, eventually, we recommend using OPC UA
security, whenever possible.

The servers will actually decide, which security modes they support. So, if you really need to make your
network secure, you can consider disabling the SecurityMode None in the servers.

The SecurityMode class contains several secure alternatives that you can choose from. It is actually a
combination of MessageSecurityMode and SecurityPolicy.

MessageSecurityMode is always either None, `Sign or SignAndEncrypt. Sign adds a digital signature to every
message, ensuring that the message contents cannot be modified during transfer. SignAndEncrypt also
encrypts the contents so that they cannot be read by third parties that might be listening to the traffic
in the network.

SecurityPolicy is a set of security algorithms that is defined in the OPC UA Specification. It has been
changing over the years, so that the two original policies, Basic128Rsa15 and Basic256 have already been
deprecated from the latest specifications, due to some details. Basic256Sha256 was added in OPC UA
1.03 and is currently the most commonly supported secure policy. The new policies,
Aes128_Sha256_RsaOaep and Aes256_Sha256_RsaPss were defined in OPC UA 1.04 to make the choices
more future proof. The 128 and 256 refer to the size of symmetric encryption keys - the shorter 128-bit
keys make communication faster, but this is seldom a real concern.

If you wish to stick to a certain SecurityMode, you can use the constant values defined in the SDK as
follows:

client.setSecurityMode(SecurityMode.BASIC256SHA256_SIGN);

or

client.setSecurityMode(SecurityMode.BASIC256SHA256_SIGN_ENCRYPT);

But in most applications, you should leave this user configurable. The SampleConsoleClient also enables
you to change it from command line, so we are initializing it with a variable:

client.setSecurityMode(securityMode);

Although, the Basic128Rsa15 and Basic256 security policies are deprecated in OPC
 UA 1.04, you may still need to use the deprecated policies, if the other applications
that you communicate with depend on them.

In practice, you can use only security modes that are enabled in the server that you are connecting to. If
you don’t know which they are, you can call

8
client.getSupportedSecurityModes();

5.2.2. HTTPS SecurityPolicy


If you use the OPC UA HTTPS transport protocol for Server Connection the UaClient will negotiate a
usable TLS security policy with the server application. You can define which policies your application
supports with

// The TLS security policies to use for OPC UA HTTPS


Set<HttpsSecurityPolicy> supportedHttpsModes = new HashSet<HttpsSecurityPolicy>();
// OPC UA HTTPS was added in UA 1.02
supportedHttpsModes.addAll(HttpsSecurityPolicy.ALL_102);
supportedHttpsModes.addAll(HttpsSecurityPolicy.ALL_103);
supportedHttpsModes.addAll(HttpsSecurityPolicy.ALL_104);
supportedHttpsModes.addAll(HttpsSecurityPolicy.ALL_105);
client.getHttpsSettings().setHttpsSecurityPolicies(supportedHttpsModes);

The constants ALL_102, ALL_103, ALL_104 and ALL_105 define which TLS security policies were
considered safe in which OPC UA specification versions.

In order to be able to make a connection with OPC UA HTTPS, you must also be able to validate the
HTTPS certificates properly. See Validating HTTPS Certificates for details about that.

In general, OPC UA HTTPS is quite tricky in practice, and it is not available in most applications, so only
use it if you really need to. Usually you should do just fine with OPC UA TCP.

5.3. User Identity


In addition to verifying the identity of the applications, OPC UA also enables verification of user
identities. In UaClient, you can define the identity of the user with the UserIdentity class. The
SampleConsoleClient does not do that by default, as each server defines what kind of user identities it
supports. You can define a user identity that uses a standard username and password combination

client.setUserIdentity(new UserIdentity("my_name", "my_password"));

Another alternative is to use a certificate and private key, similar to the application instance identity, or
a WS-SecurityToken provided by an external security system (e.g. SAML or Kerberos). To find out which
user token types are supported by the server, call

client.getSupportedUserIdentityTokens();

9
5.4. Validating Server Certificates
An integral part of all OPC UA applications, in addition to defining their own security information, is of
course, to validate the security information of the other party.

To validate the certificates of OPC UA servers, you need to define a CertificateValidator in the UaClient.
This validator is used to validate the certificates received from the servers automatically.

To provide a standard certificate validation mechanism, the SDK contains a specific implementation of
the CertificateValidator, the DefaultCertificateValidator. You can create the validator as follows:

// Use PKI files to keep track of the trusted and rejected server
// certificates...
final PkiDirectoryCertificateStore certStore = new PkiDirectoryCertificateStore();
final DefaultCertificateValidator validator = new DefaultCertificateValidator(certStore);
client.setCertificateValidator(validator);

The way this validator stores the received certificates is defined by the certStore, which in the example is
an instance of PkiDirectoryCertificateStore. It keeps the certificates in files in specific directories, such as
'PKI/CA/certs' and 'PKI/CA/rejected'. The trusted certificates are stored in the 'certs' folder and the
untrusted in 'rejected'. By default, the certificates are not trusted so they are stored in 'rejected'. You
can then manually move the trusted certificates to the 'certs' directory.

Additionally, you can plug in a custom handler to the Validator by defining a ValidationListener:

validator.setValidationListener(validationListener);

private static DefaultCertificateValidatorListener validationListener = new MyCertificateValidationListener();

where

/**
* A sampler listener for certificate validation results.
*/
public class MyCertificateValidationListener implements DefaultCertificateValidatorListener {

@Override
public ValidationResult onValidate(Cert certificate, ApplicationDescription applicationDescription,
EnumSet<CertificateCheck> passedChecks) {
// Called whenever the PkiFileBasedCertificateValidator has
// validated a certificate {
[...]
}

The SampleConsoleClient application uses the listener to prompt the user how previously untrusted
certificates should be treated. The user can accept the certificate permanently, just once or reject it. In
the first case, the certificate is placed in the 'certs' folder automatically, and in the latter cases, it is

10
placed in the 'rejected' folder. In the last case, connection to the server is cancelled due to the
certificate rejection.

You are, of course, free to use the listener to define any custom logic, but in principle, you should only
trust certificates for which passedChecks equals CertificateCheck.COMPULSORY. Although self-signed
certificates should not be used in proper onsite installations, that check is not included in the
COMPULSORY definition. Most OPC UA certificates are still self-signed, because they are easy to generate
automatically. A proper Certificate Authority should be preferred in real systems, though, to enable a
proper, centrally administered certificate management.

In addition to the checks provided by the CertificateValidator, the client should also
verify that the hostname (or IP address) of the connection address is found from
the certificate DNS names (in Subject Alternative Names). This is similar to how
 web browsers validate HTTPS connections: they trust a set of known CA certs,
validate that the web site had a certificate signed by one of those well known CAs
and also check that was the certificate for that site.

The hostname check for OPC UA Application Instance Certificates is used for a similar purpose. But in
practice this is only really useful when CA certs are used (as should be the case in real world
installations). The SDK does not provide any implementation for this check, yet, so it must be done on
the application level. See MyCertificateValidationListener.validateHostnameOfCertificate(Cert) for details.
Also, note that the users of your application should be able to suppress or disable this check, for use
cases where it does not apply.

5.4.1. Validating HTTPS Certificates


OPC UA HTTPS connections are secured on the transport level using HTTPS Certificates (TLS certificates,
in practice). Only the clients need to validate the servers' HTTPS Certificates.

In addition, the Application Instance Certificates may be used to authenticate OPC UA applications as
with OPC UA TCP. This will happen, if the MessageSecurityMode is not None. Since messages are always
encrypted on the transport level, it is enough to use MessageSecurityMode Sign to enable application
authentication.

In order to validate the servers' HTTPS Certificates, you can plug in a Certificate Validator with
HttpsSettings like this:

client.getHttpsSettings().setHttpsCertificateValidator(validator);

You can use the same validator that you use to validate Application Instance Certificates with OPC UA
TCP (see above).

Typically, HTTPS certificates are signed with CA certificates and some applciations only accept CA signed
HTTPS certificates in general, so in order to trust a HTTPS certificate of a server, the respective CA
certificate must be trusted first.

5.5. Teach Yourself the Security Details


OPC UA uses security heavily to guarantee that the applications can be safely used in real production

11
environments. The security only works when configured properly, so you should make yourself familiar
with the concepts and learn to configure these systems.

Read the OPC Unified Architecture book by Mahnke et al., for example, for more details on the OPC UA
security settings and how they should be applied. The security technology follows standard PKI (Public
Key Infrastructure) principles, so all material related to that can also be used to understand the basics.

Also, try different settings in different environments, so that you know more than you guess.

6. Connecting and Disconnecting


Once you have managed to get over the first compulsory hurdles of defining where and how to
connect, you can simply connect to the server with

client.connect();

If that fails, you will get an exception. If the actual connection cannot be made, you will get a
ServerConnectionException. If you get a connection, but something goes wrong in the server, the UaClient
typically throws a ServiceException. You may also see a ServiceFaultException, ServiceResultException or
some other runtime exception, although they are typically coming from the lower level communication
stack and should usually be handled by the SDK layer.

Often the original exception from the communication stack is available as the
 Cause of the ServiceException.

Once you have the connection, you can start playing with the server.

Once your application is finished and is ready to disconnect from the server, you can disconnect the
client by simply calling

client.disconnect();

When running the SampleConsoleClient application, you are presented with a menu


in the console output where you can pick up the tasks you may want to try out. If
you check the source code of mainMenu(), you can find the respective sample code
for the different functionalities.

6.1. Connection Monitoring


Each service call that you make to the server can fail, for example, if the connection is lost due to
network problems or the server is simply shut down.

6.1.1. ServiceException
The service calls (described in the following sections) raise a ServiceException in case of communication
or other service errors.

12
6.1.2. Timeout
The SDK handles temporary communication breaks by automatically re-establishing a lost connection.
Every service call is also monitored, and if the response takes longer than a client defined timeout, a
ServiceException with Bad_Timeout is thrown. You can define the default timeout (in milliseconds) to use
in the UaClient with:

client.setTimeout(30000);

6.1.3. Server Status Monitoring


When connected to a server, the UaClient periodically monitors the value of ServerStatus, which is a
compulsory Object in the OPC UA Server Address Space. It will perform a check every StatusCheckInterval
(1 second by default). The client uses a specific timeout setting, StatusCheckTimeout (10 seconds by
default) to detect communication breaks.

You can listen to changes in the status by defining your own ServerStatusListener, for example as follows:

/**
* A sampler listener for server status changes.
*/
public class MyServerStatusListener implements ServerStatusListener {
@Override
public void onShutdown(UaClient uaClient, long secondsTillShutdown, LocalizedText shutdownReason) {
// Called when the server state changes to Shutdown
SampleConsoleClient.printf("Server shutdown in %d seconds. Reason: %s\n", secondsTillShutdown,
shutdownReason.getText());
}

@Override
public void onStateChange(UaClient uaClient, ServerState oldState, ServerState newState) {
// Called whenever the server state changes
SampleConsoleClient.printf("ServerState changed from %s to %s\n", oldState, newState);
if (newState.equals(ServerState.Unknown)) {
SampleConsoleClient.println("ServerStatusError: " + uaClient.getServerStatusError());
}
}

@Override
public void onStatusChange(UaClient uaClient, ServerStatusDataType status, StatusCode code) {
// Called whenever the server status changes, typically every
// StatusCheckInterval defined in the UaClient.
// SampleConsoleClient.println("ServerStatus: " + status + ", code: " + code);
}
}

13
You can then set the client to use the new listener with:

protected ServerStatusListener serverStatusListener = new MyServerStatusListener();


...
client.addServerStatusListener(serverStatusListener);

6.1.4. Automatic Reconnect


The UaClient enables automatic reconnections in case the communication fails. Whenever the status
read fails due to a connection or timeout error or if the server notifies about a shutdown, the UaClient
will start to perform reconnect attempts every second in accordance to the procedure suggested in the
OPC UA specifications.

If you wish to disable the automatic reconnect feature, call UaClient.setAutoReconnect(false). In this case,
you can try to reconnect yourself by calling UaClient.reconnect() until it succeeds.

7. OPC UA Server Address Space


OPC UA server applications provide all the data that they have available in their address space which is
constructed in a standardised way according to the OPC UA Address Space Model. This helps client
applications locate all relevant data from the server even if they don’t have any prior knowledge about
it.

The OPC UA client applications identify data in the OPC UA server using Node Identifiers (NodeIds).
NodeIds are used to uniquely identify all information in the address space which consists of Nodes that
can be Objects, Variables or various Types. Nodes have Attributes that define their properties and, for
example, contain the data that the Node provides (in the Value Attribute). Nodes are connected to each
by References that signify their relationship, for example, the HasComponent Reference connects an
Object and a Variable inside it. NodeIds are used when the client sends read or write requests to the
server, for example. If the client applications don’t have the NodeId of a certain Node available, they can
browse the server address space to find it.

You can use the Prosys OPC UA Browser application to explore the address space of any OPC UA server
visually and access the information inside it.

7.1. Browse the Address Space


Typically, the first thing to do is to find the server items you wish to read or write. The OPC UA address
space is a bit more complex structure than you might expect, but nevertheless, you can explore it by
browsing.

In the UaClient, the address space is accessed through the AddressSpace property. You can call browse() to
request Nodes from the server.

The SampleConsoleClient uses an internal variable, called nodeId to keep track of the "Current Node" that
is the target of all client operations. The nodeId is initialized to the value Identifiers.RootFolder, which is a
standard NodeId defined in the OPC UA specification. It corresponds to the root of the address space,
which all servers must support. In addition, the servers must provide three standard subfolders under

14
the RootFolder: ObjectsFolder, TypesFolder and ViewsFolder. You can start browsing from one of these to
dynamically explore the available data and metadata (such as types) available from the server.

The Identifiers class is generated in the SDK to contain the NodeId of every Node
 that is defined in the OPC UA Core Specification.

So, in order to browse the address space with the SampleConsoleClient, you start from the RootFolder
and follow References between the Nodes. There may be a huge number of References from a Node,
so you can define some communication limits to the server. You can set these with the different
properties of the AddressSpace, e.g.:

client.getAddressSpace().setMaxReferencesPerNode(1000);
client.getAddressSpace().setReferenceTypeId(Identifiers.HierarchicalReferences);

by which you define a limit of 1000 References per call to the server and that you only wish to receive
the hierarchical References between the Nodes.

The AdressSpace will in fact use the MaxReferencesPerNode internally while


communicating with the server. In practice, it will make subsequent calls to the
 server until it receives all the References, even in the case that the current Node
has more References than defined by the limit. The limit is just necessary to avoid
timeouts and too large messages in the communication.

Now, if you call

List<ReferenceDescription> references = client.getAddressSpace().browse(nodeId);

you will get a list of ReferenceDescription entries from the server. From these, you can find the target
Nodes, which you can browse next. In the SampleConsoleClient, you may choose which Node to browse
next, or to end browsing and stay at the Node you are at that point. Check the sample code to see the
specifics of the methods that are used to let you browse around the address space.

7.2. Browsing Through the Nodes


An alternative way to browsing using specific NodeIds is to follow the references between Node objects.
You can access the References simply with node.getReferences(), for example. See the section Using Node
Objects for more about that.

7.3. Translate BrowsePath to NodeId(s)


OPC UA has also a specific service that enables you to find the NodeId of a node by following a
BrowsePath - a sequence of references from another node. This is most often used to locate
components and properties of object and variable instances, corresponding to a known structure that
is defined in the respective type definition. But, you can use it to find nodes, if you know their relative
location anywhere in the address space.

15
The AddressSpace object has methods translateBrowsePathToNodeId and
translateBrowsePathsToNodeIds that you can use. As input, you will need to prepare a list of
RelativePathElement structures and as a result you will get an array of BrowsePathTarget structures. In the
sample, we prompt the user for s path string and we split that using '/' characters. We also assume that
the path should be followed using HierarchicalReferences only. But in real, you can defined any
references, for each RelativePathElement.

String browsePathString = readInput(false);

List<RelativePathElement> browsePath = new ArrayList<RelativePathElement>();


for (String s : browsePathString.split("/")) {
final QualifiedName targetName = QualifiedName.parseQualifiedName(s);
browsePath.add(new RelativePathElement(Identifiers.HierarchicalReferences, false, true, targetName));
}
// The result may always contain several targets (if there are nodes with
// the same browseName), although normally only one is expected.
BrowsePathTarget[] pathTargets;
try {
pathTargets =
client.getAddressSpace().translateBrowsePathToNodeId(nodeId, browsePath.toArray(new
RelativePathElement[0]));
for (BrowsePathTarget pathTarget : pathTargets) {
String targetStr = "Target: " + pathTarget.getTargetId();
if (!pathTarget.getRemainingPathIndex().equals(UnsignedInteger.MAX_VALUE)) {
targetStr = targetStr + " - RemainingPathIndex: " + pathTarget.getRemainingPathIndex();
}
println(targetStr);
}
} catch (StatusException e1) {
printException(e1);
}

8. Read Values
Once you have a Node selected, you can read the Attributes of the Node. There are actually several
alternative read calls that you can make in the UaClient. In SampleConsoleClient we use the basic

DataValue value = client.readAttribute(nodeId, attributeId);

which reads the value of a single Attribute from a Node in the server. The Attribute to read is defined by
the attributeId. Valid IDs are defined in the Attributes class. Note that different Node types (or
NodeClasses according to the OPC UA terminology) support different Attributes. For example, the
Attributes.Value attribute is only supported by the Variable and VariableType Nodes.

In general, you should avoid calling the read methods for individual items. If you need to read several

16
items at the same time, you should use readAttributes() (to read several Attributes from one Node),
readValues() (to read the Value Attribute for several Variables) or consider using read(). The read() method
is a bit more complicated to use, but it will only make a single call to the server to read any number of
Attributes of any Nodes.

If you actually want to monitor Variables that are changing on the server, you had better use the
Subscriptions, as described below in Subscriptions.

The method will throw ServiceException or StatusException if the call does fail (see Exceptions When
Operations Fail for more information).

9. Write Values
Similar to reading, you can also write values to the server. For example:

boolean status = client.writeAttribute(nodeId, attributeId, value);

The method will throw ServiceException or StatusException if the call does fail (see Exceptions When
Operations Fail for more information).

The return value will indicate if the write operation completes successfully and synchronously (true) or
completes asynchronously (false).

Similar to the read methods, you also have better options for writing several values at the same time:
writeAttributes(), writeValues() and the generic write().

10. Exceptions When Operations Fail


If any service call or operation fails, you will get an Exception. For service call errors, such as when the
server could not handle the service request at all, you can expect a ServiceException. When performing a
single operation, any failure (for example calling readAttribute() with an invalid nodeId or attributeId or
calling writeValue() to a variable that does not permit changes) will produce a StatusException.

If you perform several operations inside a single call (such as readValues()), you can only expect a
ServiceException. For each operation you will get a StatusCode that indicates which individual operation
succeeded and which failed. Use StatusCode.isBad() and .isGood() to check whether the operation failed or
not. The StatusCode provides a complete status code, which you can check against all status codes
defined in the OPC UA specification. In case of failure, you may also get additional information in a
DiagnosticInfo structure. These fields are present in the exceptions. You can also examine the result
codes of the last service call from client.getLastServiceDiagnostics() and getLastOperationDiagnostics().

StatusCode defines actually three different severities: Good, Bad and Uncertain.
Good usually comes without additional information, whereas Bad and Uncertain
will also include a more detailed error code. The status codes defined in the OPC
 UA Core Specification are available from the SDK class StatusCodes. The actual
values are UnsignedIntegers to which you can compare them. For example
StatusCodes.Bad_UserAccessDenied.equals(exception.getStatusCode().getValue()).

17
11. Subscriptions
In addition to reading and writing, OPC UA defines a mechanism to monitor data changes and events
via subscriptions. In order to do that, you will have to create the respective Subscription instances and
add them to the client.

The subscriptions use monitored items to define individual variables or objects that you can monitor
for data changes or events, respectively.

11.1. Subscribe to Data Changes


To monitor data changes in any Variable, you can use a MonitoredDataItem. For example:

subscription = new Subscription();


MonitoredDataItem item = new MonitoredDataItem(nodeId, attributeId, MonitoringMode.Reporting);
subscription.addItem(item);
client.addSubscription(subscription);

The subscription defines the default monitoring properties, namely the PublishingInterval that are
shared by all items. Alternatively, the items may define individual details through their own properties.

Once the subscription and monitored items are defined, you can just listen to data change notifications
via a DataChangeListener:

item.setDataChangeListener(dataChangeListener);

11.1.1. DataChangeListener
The listener is defined as follows:

protected MonitoredDataItemListener dataChangeListener = new MyMonitoredDataItemListener(this);

where

18
/**
* A sample listener for monitored data changes.
*/
public class MyMonitoredDataItemListener implements MonitoredDataItemListener {
private final SampleConsoleClient client;

public MyMonitoredDataItemListener(SampleConsoleClient client) {


this.client = client;
}

@Override
public void onDataChange(MonitoredDataItem sender, DataValue prevValue, DataValue value) {
SampleConsoleClient.println(client.dataValueToString(sender.getNodeId(), sender.getAttributeId(), value));
}
};

You can use the same listener with all your items, of course.

Alternatively, you can use a listener in the Subscription instance, to get notifications of complete
messages sent by the server. The SampleConsoleClient demonstrates both alternatives, but uses mainly
the item-based listener.

11.2. Subscribe to Events


In order to listen to events in Event Notifier Objects, you can use MonitoredEventItems.

Basically, all objects that send events, should have the SubscribeToEvents bit set in their EventNotifier
attribute. But, since not all servers obey this rule, you can always try to monitor any object and we also
ignore that in our example.

First of all, you must define an EventFilter that specifies more details on which events and which data
from them you wish to monitor. To start, you must define the SelectClauses for the filter. They will
specify the event fields that you wish to monitor. Additionally, you may also define WhereClauses, which
you can use to actually choose which exact events you wish to receive.

In the SampleConsoleClient, we have defined the event fields using a custom manner as follows:

protected final QualifiedName[] eventFieldNames = {


new QualifiedName("EventType"), new QualifiedName("Message"),
new QualifiedName("SourceName"), new QualifiedName("Time"),
new QualifiedName("Severity"), new QualifiedName("ActiveState/Id") ,
null, null
};

19
The last two fields are left as place holders for two custom fields. These are
initialized at run-time, because the QualifiedName-identifiers need a
 namespaceIndex which depends on the dynamic index in the server. See the code
sample for details in initEventFieldNames().

The createBrowsePath() method used in the SampleConsoleClient is a special feature

 of the sample. It breaks each QualifiedName to a BrowsePath, i.e. an array of


QualifiedNames, using the ‘/’ characters as delimiters. We will need the
BrowsePaths when we define the fields, in practice.

11.2.1. SelectClauses
So, we can define SelectClauses for the filter like this:

NodeId eventTypeId = Identifiers.BaseEventType;


UnsignedInteger eventAttributeId = Attributes.Value;
String indexRange = null;
SimpleAttributeOperand[] selectClauses = new SimpleAttributeOperand[eventFields.length + 1];

for (int i = 0; i < eventFields.length; i++) {


QualifiedName[] browsePath = createBrowsePath(eventFields[i]);
selectClauses[i] =
new SimpleAttributeOperand(eventTypeId, browsePath, eventAttributeId, indexRange);
}
// Add a field to get the NodeId of the event source
selectClauses[eventFields.length] =
new SimpleAttributeOperand(Identifiers.ConditionType, new QualifiedName[0], Attributes.NodeId,
null);

// Create the filter


EventFilter filter = new EventFilter();
// Select the event fields
filter.setSelectClauses(selectClauses);

11.2.2. WhereClause
And next we filter the events we want to receive using the WhereClause (this is optional - if you don’t
define the WhereClause, you will receive all events without further filtering):

20
// Event filtering: the following sample creates a
// "Not OfType GeneralModelChangeEventType" filter
ContentFilterBuilder fb = new ContentFilterBuilder();

// The element operand refers to another operand -


// operand #1 in this case which is the next,
// LiteralOperand
fb.add(FilterOperator.Not, new ElementOperand(
UnsignedInteger.valueOf(1)));
final LiteralOperand filteredType = new LiteralOperand(
new Variant(Identifiers.GeneralModelChangeEventType));
fb.add(FilterOperator.OfType, filteredType);
filter.setWhereClause(fb.getContentFilter());

This one just filters out possible ModelChangeEvents. There are various operators that you can use.
Most of them require two arguments, for example fb.add(FilterOperator.Equals, operand1, operand2).

11.2.3. MonitoredEventItem
So, finally we are ready to create the event item using the filter that was just created and the NodeId of
the object we wish to monitor:

MonitoredEventItem eventItem = new MonitoredEventItem(nodeId, filter);


eventItem.addEventListener(eventListener);
subscription.addItem(eventItem);

If you don’t know any better, you can always monitor the Server object (Identifiers.Server), in which case
you will receive all events from the server.

The event listener is defined as follows, and used to react to the event notification:

protected final MonitoredEventItemListener eventListener = new MyMonitoredEventItemListener(this,


eventFieldNames);

where

21
/**
* A sampler listener for monitored event notifications.
*/
public class MyMonitoredEventItemListener implements MonitoredEventItemListener {
private final SampleConsoleClient client;
private final QualifiedName[] requestedEventFields;

/**
* @param client
* @param eventFieldNames
*/
public MyMonitoredEventItemListener(SampleConsoleClient client, QualifiedName[] requestedEventFields)
{
super();
this.requestedEventFields = requestedEventFields;
this.client = client;
}

@Override
public void onEvent(MonitoredEventItem sender, Variant[] eventFields) {
SampleConsoleClient.println(client.eventToString(sender.getNodeId(), requestedEventFields, eventFields)
);
}
};

11.2.4. Alarms and Condition Refresh


OPC UA alarms are also monitored via events. If you expect alarm notifications from the server, you
should also use the ConditionRefresh method to request the currently active alarms to be sent, after
you have created a MonitoredEventItem. The SDK enables this simply with

subscription.conditionRefresh();

If the server supports the service, you should see something like this in the SampleConsoleClient:

Node: i=2253 Fields: EventType=RefreshStartEventType {i=2787}; Message=Refresh Start;


SourceName=Server; Time=09/06/23 15:19:40.9460000 GMT; Severity=1; ActiveState/Id=null;
MyVariable=null; MyProperty=null; ConditionId=i=0
Node: i=2253 Fields: EventType=ExclusiveLevelAlarmType {i=9482}; Message=Level exceeded;
SourceName=MyLevel; Time=09/06/23 15:19:26.9300000 GMT; Severity=500; ActiveState/Id=false;
MyVariable=null; MyProperty=null; ConditionId=2:MyLevelAlarm {ns=2;s=MyLevel.Alarm}
Node: i=2253 Fields: EventType=RefreshEndEventType {i=2788}; Message=Refresh End; SourceName=Server;
Time=09/06/23 15:19:40.9470000 GMT; Severity=1; ActiveState/Id=null; MyVariable=null; MyProperty=null;
ConditionId=i=0

22
11.3. SubscriptionAliveListener
You may also wish to listen to the alive and timeout events in the subscription. These will help you
verify that the server is actively monitoring the values, even in the case that the values are not actually
changing and therefore new data change notifications are not being sent. The example below
demonstrates how to add such a listener to a subscription:

protected SubscriptionAliveListener subscriptionAliveListener = new MySubscriptionAliveListener();


...
subscription.addAliveListener(subscriptionAliveListener);

The functionality related to different events must be implemented in the respective methods of the
listener class:

23
/**
* A sample listener for subscription alive events.
*/
public class MySubscriptionAliveListener implements SubscriptionAliveListener {

private static final ZoneId TIME_ZONE = ZoneId.systemDefault();


private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME
.withZone(TIME_ZONE);

private static void printInfo(String event, Subscription subscription) {


String lastAlive = subscription.getLastAlive() == null ? "null" : FORMATTER.format(subscription
.getLastAlive());
SampleConsoleClient.println(String.format("%s Subscription %s: ID=%d lastAlive=%s", FORMATTER.
format(Instant.now()),
event, subscription.getSubscriptionId().getValue(), lastAlive));
}

@Override
public void onAfterCreate(Subscription subscription) {
// the subscription was (re)created to the server
// this happens if the subscription was timed out during
// a communication break and had to be recreated after reconnection
printInfo("created", subscription);
}

@Override
public void onAlive(Subscription subscription) {
// the server acknowledged that the connection is alive,
// although there were no changes to send
printInfo("alive", subscription);
}

@Override
public void onLifetimeTimeout(Subscription subscription) {
printInfo("lifetime ended", subscription);
}

@Override
public void onTimeout(Subscription subscription) {
// the server did not acknowledge that the connection is alive, and the
// maxKeepAliveCount has been exceeded
printInfo("timeout", subscription);
}

24
11.4. Transferring Subscriptions Between Sessions and
Automatic Reconnections
The Subscription is always attached to a client Session, but you can transfer the Subscription to another
Session, if necessary.

The SDK typically handles connection breaks automatically and creates new sessions whenever
required. It can also transfer the Subscription automatically to a new Session when required or even
create a new Subscription with new MonitoredItems, if the Subscription also times out.

Additionally, you can create a new UaClient instance and use the Subscription.transferTo(UaClient) method
to move the Subscription to the new client. It is recommended to connect the new client to the server
first.

transferTo will use the OPC UA TransferSubscription service to enable seamless continuation of
monitoring in fail-over situations. The SDK only enables that within the same application, though, using
the Subscription and MonitoredItems objects.

Should you need to move a Subscription to another, redundant, client application, you will need to
exchange information about the MonitoredItems between the applications. This will enable creating a
new Subscription with similar parameters in the other client, but not the TransferSubscription service.

12. History Access


OPC UA servers may also provide history information for the Nodes, including historical time series
data and events.

12.1. Reading History


To actually read history data, you have several options. The basic way is to use UaClient.historyRead(),
which is recommended if you need to do several readings at once. This example reads a complete
history for a single Node (specified by nodeId):

HistoryReadDetails details = new ReadRawModifiedDetails(false,


DateTime.MIN_VALUE, DateTime.currentTime(),
UnsignedInteger.MAX_VALUE, true);
HistoryReadValueId nodesToRead = new HistoryReadValueId(
nodeId, null,
QualifiedName.DEFAULT_BINARY_ENCODING, null);
HistoryReadResult[] result = client.historyRead(details,
TimestampsToReturn.Both, true, nodesToRead);

HistoryData d = result[0].getHistoryData().decode();
DataValue[] values = d.getDataValues();

What you need to be aware of is that there are several “methods” that the historyRead() actually
supports, depending on which HistoryReadDetails you use. For example, in the above example we used
ReadRawModifiedDetails, to read a raw history (the same structure is used to read modified history as

25
well, therefore the name).

12.1.1. Reading Variable History


To make your life a bit easier, UaClient also defines several convenience methods to make specific
history requests.

First of all, you need to check, whether the variable has history:

if (variable.getUserAccessLevel().contains(AccessLevelType.Options.HistoryRead)) ...

This verifies that the variable has history and it is accessible with your current user account.

After this, you can use a few different methods.

To read the collected samples (raw history) for a certain interval, you can use historyReadRaw:

DateTime endTime = DateTime.currentTime();


DateTime startTime = endTime.minus(30, ChronoUnit.MINUTES);

DataValue[] result = client.historyReadRaw(nodeId,


startTime, endTime, UnsignedInteger.valueOf(1000), true, null,
TimestampsToReturn.Source);

Here the UnsignedInteger.valueOf(1000) defines the maximum number of values to request from the
server per call. Eventually, it will try to read all values within the interval, but it can break the call to
several requests using this parameter. Note that if the interval is long and there is a lot of data, this can
also take quite long to perform.

Alternatively, you can read values corresponding to certain time stamps with historyReadAtTimes:

DateTime[] reqTimes = new DateTime[] {startTime, endTime};


values = client.historyReadAtTimes(nodeId, reqTimes, null, TimestampsToReturn.Source);

The server should adjust the raw sample data to match with the exact time stamps, possibly by linear
interpolation.

If the server supports Aggregate Functions (Average, Min, Max, Count, etc.), you can also use
historyReadProcessed. Check the SampleConsoleClient.readHistory for a complete example.

12.1.2. Reading Event History


Event history can be read for Objects that have recorded history in the server. You can find that out, by
checking whether their EventNotifier attribute contains HistoryRead.

if (object.getEventNotifier().contains(EventNotifierType.Options.HistoryRead)) ...

26
You must define an EventFilter, the same way that you do when you Subscribe to Events. In the
SampleConsoleClient, we have a method that can provide the filter:

EventFilter eventFilter = createEventFilter(eventFieldNames);

Then you can read the events using:

DateTime endTime = DateTime.currentTime();


DateTime startTime = endTime.minus(1, ChronoUnit.HOURS);

HistoryEventFieldList[] events = client.historyReadEvents(nodeId, startTime, endTime,


UnsignedInteger.valueOf(1000), eventFilter, TimestampsToReturn.Source);

12.2. Updating or Deleting History


To modify existing history data in the server, you can use the historyUpdate() method or, again, one of
the convenience methods that provide you with more semantics. See the documentation for the
various historyUpdateXxx() and historyDeleteXxx() methods in UaClient for more about those.

13. Calling Methods


OPC UA also defines a mechanism to call Methods in the server Objects.

To find out if an Object defines Methods, you can call

List<UaMethod> methods = client.getAddressSpace().getMethods(nodeId);

UaMethod is a Node object, which gets stored into the NodeCache (see Using Node Objects). If you wish
to perform a light browse, you can just call:

List<ReferenceDescription> methodRefs = client.getAddressSpace().browseMethods(nodeId);

to get a list of the Method References from the Node.

The UaMethod is initialized with the InputArguments and OutputArguments properties, which you can
examine for the argument name, type, etc.

Argument[] inputArguments = method.getInputArguments();


Argument[] outputArguments = method.getOutputArguments();

To actually call the Method, you need to provide a valid value (as Variant) for each of the
InputArguments, and just call it:

27
Variant[] outputs = client.call(nodeId, methodId, inputs);

As a result you get values for the OutputArguments.

Note also that you will usually need to use the DataTypeConverter to convert the inputArguments to the
correct data type, before calling the Method. The OPC UA specification defines that the server may not
convert the arguments, if they are provided with incorrect data types. So you will get
Bad_InvalidArgument errors for each argument that is not provided in the correct data type. See the
sample code (inside SampleConsoleClient.readInputArguments()) for more details.

14. Registering and Unregistering Nodes


These services are meant for improved performance. You can request the server to prepare some
Nodes to which you will refer often in your client application by registering them with the
RegisterNodes service call. The server may also define new and more efficient NodeIds for the Nodes
and the client can then use the new NodeIds instead of the NodeIds it received by browsing the address
space.

You can access these from the AddressSpace. To register a Node for quick access, call:

NodeId[] registeredNodeId = client.getAddressSpace().registerNodes(nodeId);

When you are done, you can unregister the Nodes using, for example:

NodeId[] nodes = client.getAddressSpace().unregisterAllNodes();

These methods are not usually necessary and not always supported by the servers

 anyway, so you can usually ignore them. If the server manufacturer suggests, you
could consider using them.

15. Using Node Objects


The AddressSpace object in the UaClient can also cache Nodes on the client side. These UaNode objects
will help you to browse the address space and to use the information in your application more
conveniently.

You can simply request the Node objects from the address space using the methods getNode(), getType(),
getMethods(), etc.

To see it in action, just go and explore the sample code in more detail – especially the methods
printCurrentNode() and referenceToString().

getNode() is even more useful when used together with code generation. If you register generated
classes, you can use complete UA types in your application through the respective Java classes. Read on
to learn more about that.

28
16. Information Modeling and Code Generation
The Prosys OPC UA SDK for Java supports loading existing OPC UA information models in the standard
OPC UA NodeSet2 XML files ('NodeSets' in the following text). It also supports code generation from the
NodeSets, to create Java classes corresponding to the OPC UA Object and Variable Types. By default,
type definitions from the Core Specification is already generated for the SDK.

You can use other information models respectively by importing type information from the NodeSets
and by using the Code Generator to generate the respective Java classes to work with. Although this is
mostly useful on the server side, you can also take advantage of these features in the client
applications.

To create your own information models, you can use the OPC UA Modeler application.

16.1. Prosys OPC UA SDK for Java Code Generator


You can generate Java classes based on information models stored in the NodeSet files with the Code
Generator provided with the Prosys OPC UA SDK for Java. The Code Generator is located in the
'codegen' folder of the distribution package.

For instructions on using the Code Generator provided with the Prosys OPC

 UA SDK for Java, please refer to the Code Generator Manual in the 'codegen'
folder of the distribution package.

Follow the instructions in the included manual and experiment with the samples to learn how to
configure and execute the code generation procedure. Then you may return to this tutorial and read
the following sections on how to utilize the generated classes in your own applications.

16.2. Using the Generated Classes in Applications


The Java classes generated with the Code Generator enable simple usage of the OPC UA information
models. With the generated classes, the client application is able to handle the instances of the custom
types in OPC UA servers as Java objects.

This section explains how the Java classes generated using the Code Generator can be imported and
utilized in applications developed with the Prosys OPC UA Client SDK for Java.

16.3. Registering the Model


The generated Java classes for ObjectTypes and VariableTypes are more extended versions of the
standard UaNode implementations. In order to let the SDK use the generated classes, the SDK must be
made aware of the generated classes. This is done by registering the model.

16.4. Using Instances of Generated Types


The client implementation classes are generated into 'client' sub-packages under the defined
generation folders.

To use the generated Java classes in your applications:

29
1. Add the generated files to your project source path.
2. Register generated classes with UaClient.registerModel(CodegenModel model). For example:

client.registerModel(example.packagename.client.ClientInformationModel.MODEL);

The Code Generator has also an option for Automatic Model Registration. This

 is very useful, especially, when you are using a bigger number of models.
Please see the Codegen Manual for more details.

3. Read instances from the server with client.getAddressSpace().getNode(NodeId id). You have to cast the
result to the correct generated type. Alternatively you can pass a Class parameter of the correct
type as additional parameter, for example:

AnalogItemType node = client.getAddressSpace().getNode(<NodeId>, AnalogItemType.class);

The SDK distribution package provides a sample information model in the 'SampleTypes.xml' file that is
in the 'models' folder (both the command line and Maven version of Codegen). A complete example of
the procedure for using an instance of the ValveObjectType from the 'SampleTypes.xml' model is
demonstrated in the following example:

// 1. Register the generated classes in your UaClient object by


// using the ClientInformationModel class that is generated in the client package.
client.registerModel(example.packagename.client.ClientInformationModel.MODEL);

// 2. Get a node from the server using an AddressSpace object.


// Give the nodeId of the instance as a parameter.
// Cast the return value to the corresponding generated class
ValveObjectType sampleValve =
(ValveObjectType) client.getAddressSpace().getNode(nodeId);

// 3. Use the instance.


// e.g. get the cached value of the PowerInput Property
sampleValve.getPowerInput();

Calling the getter for component values will internally get the node from the
AddressSpace and get the value from it. It does not directly make a read call to the
 server, unless the node is not present in the AddressSpace's NodeCache. You need to
use read() methods in the UaClient if you need the latest values.

Calling the setter for component values sets the value to the local node, i.e. it is not

 written to the server. You need to use write() methods in the UaClient to write the
value to the server.

30
17. Custom DataTypes
OPC UA Servers enable flexible rules for defining custom data types. These can be used in OPC UA
Information Models for different use cases. The servers will expose all of their type definitions so that
client applications can also interpret the custom data types automatically.

Custom data types can be defined as Simple Types, Structures, Enumerations or OptionSets.

17.1. Simple Types


The OPC UA builtin types define how to communicate integers and floating point values of different
precision, strings, byte arrays (ByteStrings) and a few custom OPC UA types, such as LocalizedText,
QualifiedName etc.

All of these can be extended in practice, but for the simple types the values don’t really change and the
custom types only define more refined semantics.

17.2. Structures
The OPC UA specification defines a DataType called Structure that can represent any complex datatype.
The custom structures are all extending this and they can consist of any number of Fields that can be of
any data type. Fields can also be arrays of any data type.

17.3. Enumerations
Enumeration types define a fixed set of values. They are transferred as Int32 (i.e. Integer) on the wire and
thus you will get it as such from UaClient.readValue(), for example.

17.4. OptionSets
OptionSet types are split into 2 categories, but both represent bitsets; collections of Boolean on/off
flags. There are both 'Structure OptionSets' and 'UInteger OptionSets'.

'Structure OptionSets' are subtypes of the OptionSet structure. 'UInteger OptionSets' are subtypes of
any UInteger type (Byte, UInt16, UInt32, UInt64) and they have an OPC UA Property called
OptionSetValues.

17.5. UaDataTypeSpecification
In order to enable processing the metadata and values of the different types, including all simple types,
the SDK defines a Java interface UaDataTypeSpecification, that has specific implementation for each
variety, as follows:

UaDataTypeSpecification implementation Usage

OptionSetStructureSpecification Models Structure(s) that are also optionsets

31
UaDataTypeSpecification implementation Usage

OptionSetSpecification Models OptionSets

StructureSpecification Models Structures

EnumerationSpecification Models Enumerations

SimpleTypeSpecification Models simple types that are none of the above

StructureSpecification and OptionSetSpecifications are interfaces, whereas the


 OptionSetStructureSpecification extends both.

SimpleTypeSpecification has only a name, TypeId, BaseTypeId and the Java class corresponding to it. All
simple types in OPC UA are encoded as their base type on the wire.

EnumerationSpecification includes the alternative Values for the specific Enumeration type.

OptionSetSpecification includes the alternative Options for the OptionSet. They are modeled as
OptionSpecification.

StructureSpecification includes all Fields for the Structure. They are modeled as FieldSpecification.

OptionSetStructureSpecification models both Fields (similar to the OptionSet Structure, which means that
new fields cannot be added to it) and Options.

The Enumeration, UaOptionSet, Structure and OptionSetStructure all have a method .specification() that can
be used to obtain the respective UaDataTypeSpecification. Additionally they have API that allows generic
processing. For example, Structure has get/set methods that can be used with a FieldSpecification or field
name. This allows building generic handling when reading the Value Attribute. Note that for Enumeration
the value is still a Integer (as they are encoded as Int32), thus you must also read the DataType Attribute,
and then find the correct EnumerationSpecification from the EncoderContext.

By default SDK reads metadata from the server as part of UaClient.connect and forms the
UaDataTypeSpecification that represents the custom types. The Prosys OPC UA SDK for Java Code
Generator also creates UaDataTypeSpecification that SDK loads when the model is registered.

17.6. EncoderContext
Every UaClient and UaServer instance uses an EncoderContext, which includes all known type
specifications. UaClient initializes them when it connects to the server as part of the TypeDictionary
initialization. Alternatively, the client can also access the type specifications directly from the
DataTypeDefinition attributes of the DataType nodes.

32

You might also like