1. Introduction
The Prosys OPC UA SDK for Java Code Generator (later just 'Codegen') can be used to reduce manual workload when using custom information models in the Prosys OPC UA SDK for Java. It creates Java classes based on OPC UA information models.
2. About Information Models
Codegen will work with most information models. If you encounter a model that does not generate properly, let us know.
2.1. Obtaining Information Models
Codegen requires OPC UA information models defined in the OPC UA NodeSet2 XML format ('NodeSet' in the following text). The NodeSet format is defined in the OPC UA Specification v1.05, Part 6., Annex F.
A popular software for creating and managing information models is the OPC UA Modeler that also enables exporting the information model in the NodeSet2 format. The exported models can then be used as the input for Codegen.
If your model depends on other information models, you will need to obtain and generate those as well. |
Some of the well known Companion Specification information models can be obtained from the OPC Foundations GitHub page: https://github.com/OPCFoundation/UA-Nodeset/. For example, the NodeSet for the well known Device Information model ("DI") is at: https://github.com/OPCFoundation/UA-Nodeset/blob/latest/DI/Opc.Ua.Di.NodeSet2.xml. The repository also contains the XML Schema Definition corresponding to the NodeSet format: https://github.com/OPCFoundation/UA-Nodeset/blob/v1.05/Schema/UANodeSet.xsd.
2.2. Known Issues
Codegen is currently supporting OPC UA Specification up to version 1.05.02. Since the specifications are still evolving over time, and the OPC Foundation is constantly adding new data types in new versions, models that take advantage of those features might not always work without further updates to the Codegen itself.
In addition, there are some well known issues, which are listed below:
-
Codegen will generate a setter and getter for each UA Variable of an UA Object, e.g.
getVar()
andsetVar(…)
. If you define an UA Method with the same name, e.g.getVar
orsetVar
, and the same argument type inside the same UA Object, then you will get a name clash. Additionally same kind of conflict can happen if a model defines names used by the SDK base API which the generated classes extend. -
The generated Java methods do not include the UA namespace in any way. This means that if an UA type defines multiple components with the same BrowseNames that only differ by their NamespaceIndices, then it will result in a name clash.
In both cases currently either the model has to be changed, or one of the components be excluded (starting from 3.1.8) from generation. In the case where there is a conflict between an UA Method and a component, we recommend excluding the component, as excluding a Method will also remove it’s handling logic. See Excluding sub-nodes under a Type node from generation for more information.
The OPC Foundation Best Practices is giving guide lines that define rules for naming OPC UA types. However, it is possible to create a NodeSet that contains multiple types with the same name (that would result into the same fully-qualified Java class name).
Codegen as of version 5.0.0 tries to resolve this by default using separate subpackages for different NodeClass (ObjectTypes, VariableTypes, DataTypes) outputs. However, if same NodeClass contains duplicate names, Codegen is not capable of handling those and will report the situation and halt.
The specification makes the option to define a SymbolicName in the model when the BrowseName is not usable in generated code. Some models are missing these, but Codegen will try to guess a suitable name if necessary. In general, it would be better to fix the model, nevertheless.
3. Using Codegen
Codegen can be currently used in two alternative ways:
In both cases, the generated code will compile and work in Java 8 or later.
3.1. Using Codegen from Command Line
In order to run Codegen from the command line you will need the java
command to be available in the PATH. Codegen requires Java 8, 11, 17 or 21. It is not tested on newer than 21, but it should work (contact support if not).
The parameters of the code generation can be modified through a configuration XML file. Please see the sample configuration files in the configexamples
folder. Also see the Configuration chapter for more general information on configuring the code generation procedure.
Codegen is designed to be launched via the supplied startup scripts that are in the codegen/bin
directory: codegen.bat
for Windows and codegen.sh
for other operating systems.
The following arguments can be passed to the scripts:
Argument | Info | ||
---|---|---|---|
-c <path> |
Path to the configuration XML file.
|
||
-v |
Output debug log |
||
-h |
Display help |
On Windows, you can run Codegen with:
codegen.bat -c path_to_configuration_file
For example, on Windows PowerShell running the Codegen for the SampleTypes.xml model (in commandline/models
folder) the call could look like this (in working directory commandline
):
.\bin\codegen.bat -c .\configexamples\sampletypesconf.xml
On other operating systems, you can run Codegen with:
./codegen.sh -c path_to_configuration_file
3.2. Using Codegen with Maven Integration
If you use Apache Maven as your build tool, you can use the included Maven plugin for Codegen.
This tutorial does not cover Maven usage in general. Please, visit Apache Maven website for more information. |
The Maven plugin for Codegen is provided in the codegen/maven-integration/maven-install-helper
folder. To install it to your .m2
local repository, go to this folder and run
mvn install
Once installed, you can test the integration with the provided sample project. Go to codegen/maven-integration-sample
folder and run
mvn generate-sources
The generated classes are not added to the build path automatically. You can use the Build Helper Maven Plugin to achieve that. |
The parameters of code generation can be modified by configuring the Codegen plugin in the associated 'pom.xml' file. See the Configuration chapter for more general information on configuring the code generation procedure.
The plugin should work on relatively recent Maven 3 versions. If you encounter an error and are using Java 11 or later, please first try updating Maven to the latest version. |
4. Configuration
This section explains some of Codegen’s configuration and usage concepts.
4.1. Mapping OPC UA NamespaceUris to Java Packages
OPC UA uses the concept of namespaces to separate between types defined in different models. Each namespaces is identified by a globally unique NamespaceUri (for example, the NamespaceUri of the OPC UA standard namespace is http://opcfoundation.org/UA/).
OPC UA Servers use NamespaceArrays, where they keep the namespaces that they support and they often use a concept called NamespaceIndex to refer to a namespace (the index for the uri in the array), instead of using the NamespaceUri directly. This saves space for the messages client and server exchange. The NodeSet files also use a local NamespaceArray and the NodeId and BrowseName definitions refer to the namespaces using the indices.
Note that the NodeSet indices are local to that file. They are not the same as the server NamespaceArray mappings! Basically each Server NamespaceArray and NodeSet file is it’s own context for the mappings. |
Thus, the Codegen cannot use NamespaceIndex, because it doesn’t know where the outputs are used and they could be potentially used in multiple conflicting NamespaceArrays. And it must load multiple NodeSets, as a NodeSet (i.e. UA namespace, in practice) can depend on another NodeSet. Thus it will always use the NamespaceUri to identify the namespaces.
Java programming language, on the other hand, uses packages to organize classes in Java namespaces. Thus, Codegen will generate classes in each OPC UA namespace to a respective Java package and the mapping between these is defined in the Codegen Configuration.
4.1.1. Namespace Prefix
Some Codegen outputs are always named the same way: Ids
, UaIds
, CommonInformationModel
, ClientInformationModel
, ServerInformationModel
, and DataTypeDictionaryHelper
. Thus, if you generate more than one model, you will end up with multiple classes with same names (in different Java packages). It is only possible to import a single one of these in Java code, thus for the rest you would need to use the fully qualified name.
You can define a unique prefix for each namespace mapping to avoid this issue. For example, for the Device Information common model typically the prefix 'Di' is used. Thus, instead of needing to use the fully qualified name org.opcfoundation.ua.di.ClientInformationModel
the prefix causes the class name to be DiClientInformationModel
, which can be imported.
SDK internally has already one set of these from the core namespace. If possible, it is recommended to set a prefix for each namespace you generate. |
4.2. Generation Targets
Codegen generates Java classes from types (ObjectTypes, VariableTypes, ReferenceTypes and DataTypes) defined in OPC UA information models.
If your models include instances (Objects and Variables), Codegen ignores these, since it can only generate Java classes for UA types. However, when you load the model to your server at startup, these instances will be created in the address space by the SDK. See the Server Tutorial for more information. |
Codegen can create the following Java interfaces and classes for each OPC UA ObjectType and VariableType that is defined in the models (using the AnalogItemType defined in the OPC UA Core model as an example):
-
Common interface that has the same name as the UA type:
AnalogItemType
-
Base class for the client side:
AnalogItemTypeImplBase
-
Actual class for the client side:
AnalogItemTypeImpl
-
Base class for the server side:
AnalogItemTypeNodeBase
-
Actual class for the server side:
AnalogItemTypeNode
The base classes should not be modified after generation. The actual classes can be modified, but note that Codegen will override the changes when you regenerate. So you should copy the actual classes to version control to protect them from unwanted changes.. |
For each UA Method defined in an ObjectType, the following Java files are also created (using the ResendData
Method of the ServerType
as an example):
-
Parameter object class for each Method that has more than one output argument. It is used as the return value for the method. It is generated as a nested class of the Common interface.
-
Helper interface for providing an external method implementation:
ServerTypeResendDataMethod
.
Additionally, the following classes are generated per model:
-
Structure
class for each UA Structure DataType -
Enumeration
enum for each UA Enumeration DataType -
UaOptionSet
class for each UA OptionSet DataType -
DataTypeDictionaryHelper
class, which is used internally by the SDK to handle custom structures -
CommonInformationModel
class, which will holdsUaDataTypeSpecifications
-
ClientInformationModel
class that is used to register the generated classes on the client side -
ServerInformationModel
class that is used to register the generated classes on the server side -
Ids
class which contains the identifiers (as ExpandedNodeId) for each generated type and their subcomponents -
UaIds
class which contains the identifiers (as UaNodeId) for each generated type and their subcomponents
If a namespace is mapped with a prefix, it will be used to prepend the Ids and InformationModel classes. For example, for a prefix Test the output would be TestIds , TestClientInformationModel and TestServerInformationModel .
|
4.2.1. Target Configuration
Codegen enables you to configure which targets you wish to generate by editing the configuration files. For this purpose, the following target names are defined:
Target | Description | ||
---|---|---|---|
common |
Generate Java interfaces for each type in the model plus Ids, UaIds, Structures, Enumerations, OptionSets and helper classes. These are not designed for editing. |
||
client_base |
Generate client-side base class for each type. Not designed for editing. |
||
client_impl |
Generate client-side actual class for each type (extends base classes, can be edited) |
||
server_base |
Generate server-side base class for each type Not designed for editing. |
||
server_impl |
Generate server-side actual class for each type (extends the base classes, can be edited) |
||
server_model |
Special Copy the used information model file to the server-side package |
||
client_model_provider, server_model_provider |
Special Writes
|
Target | Description |
---|---|
client_all |
Combination of client_base, client_impl. |
server_all |
Combination of server_base, server_impl. |
base_all |
Combination of common, client_base, server_base. |
impl_all |
Combination of client_impl, server_impl. |
all_code |
Combination of everything except server_model, i.e. all .java output. |
all_resources |
Combination of client_model_provider, server_model_provider and server_model, i.e. all other than .java output. |
all |
Combination of every target |
If you generate the base and actual (i.e. "impl") targets in different code generation configurations, remember that both are always required. |
4.3. Configuration Parameters
Codegen can be configured with a few configuration parameters. In the command line version of Codegen, the changes need to be implemented in a configuration XML file. In the Maven plugin version, the parameters are defined in the POM XML file. Please also see the provided examples for more complete examples of the configuration process. The examples for the command line version are in the 'configexamples' folder and the 'maven-integration-sample' Maven project provides examples for configuring the Maven integration.
4.3.1. Model Directories
The modelSources parameters define the directories (or files directly) where Codegen is searching for information models that are used in the code generation. The files must conform to the OPC UA NodeSet2 XML format.
In the command line version, the models are defined in the following way:
<modelSources> <modelSource>${app.home}/models</modelSource> </modelSources>
The Maven plugin is configured the same way (but you would use e.g. ${project.basedir}/models
as the setting value.
4.3.2. Namespace Mapping Parameters
The namespace mapping parameters define how the OPC UA namespaces that are defined in the information models are mapped to their respective Java packages. See chapter Mapping OPC UA NamespaceUris to Java Packages for an explanation of the concept. Each mapping consists of the following parameters:
Parameter | Description |
---|---|
uri |
The NamespaceUri associated with the information model used as the input of the code generation. |
packageName |
The name of the Java package where the Java code output for the information model is placed. |
prefix |
Optional The prefix used in the name of the |
nodeClassSpecificPackageNames |
Optional Default true if not set. Controls whether or not NodeClass-specific subpackages are used when generating. |
The namespace mapping parameters are defined as follows (In both command line version and the Maven plugin configuration):
<namespaceMappings> <namespaceMapping> <uri>http://ua.prosysopc.com/SampleTypes</uri> <packageName>example.packagename</packageName> <prefix>Sample</prefix> </namespaceMapping> </namespaceMappings>
Starting from 5.0.0, a separate subpackage is used for each NodeClass, by default. This can be disabled by setting nodeClassSpecificPackageNames
as false. For example, for the Device Information (DI) model, the configuration could look like:
<namespaceMapping> <uri>http://opcfoundation.org/UA/DI/</uri> <packageName>com.prosysopc.ua.types.di</packageName> <prefix>Di</prefix> <nodeClassSpecificPackageNames>false</nodeClassSpecificPackageNames> < /namespaceMapping>
Note that this will still create a separate subpackage for the client and server specific implementations:
com.prosysopc.ua.types.di com.prosysopc.ua.types.di.client com.prosysopc.ua.types.di.server
By default (when nodeClassSpecificPackageNames
is true), the following packages will be created:
com.prosysopc.ua.types.di com.prosysopc.ua.types.di.datatypes com.prosysopc.ua.types.di.objecttypes com.prosysopc.ua.types.di.variabletypes com.prosysopc.ua.types.di.client com.prosysopc.ua.types.di.objecttypes.client com.prosysopc.ua.types.di.variabletypes.client com.prosysopc.ua.types.di.server com.prosysopc.ua.types.di.objecttypes.server com.prosysopc.ua.types.di.variabletypes.server
Note that data types do not have any client/server specific subpackages as they are common for both.
4.3.3. Generation Target Parameters
The concept of the generation target is explained in more detail in the chapter Generation Targets.
Parameter | Description |
---|---|
targets |
Defines what is the output of the code generation, a comma separated list of Generation Targets |
uri |
The NamespaceUri associated with the information model used as the input of the code generation |
outputs |
Definition of output folders (see below examples) |
timestampOutputs |
Optional (default true), creates timestamp of the generation in the javadoc of generated classes. Set to 'false' to disable. |
In the command line version, the generation target parameters are defined in the following way:
<generates> <generate> <targets>all</targets> <uris> <uri>http://ua.prosysopc.com/SampleTypes</uri> </uris> <outputs> <code>${app.home}/sampletypes/output_code</code> <resources>${app.home}/sampletypes/output_resources</resources> <outputs> </generate> <generates>
In the Maven plugin, the configuration is the same, but typically you would define the output locations as properties e.g. ${codegen.output.code}
and ${codegen.output.resources}
, that would be then defined e.g. as:
<properties> ... <codegen.output.code>${project.build.directory}/codegenoutput/code</codegen.output.code> <codegen.output.resources>${project.build.directory}/codegenoutput/resources</codegen.output.resources> ... </properties>
Then you would use the build-helper-maven-plugin to include them as extra sources and resources. See the 'codegen/maven-integration/maven-integration-sample/pom.xml' for more information.
4.3.4. Excluding sub-nodes under a Type node from generation
The generation of some models might result in generated code that conflicts with the base SDK API. Or there might be a name clash when generating more than one model. Currently the only workaround (outside of editing the model itself) is to exclude those nodes when generating get/set/getXXXNode methods for the type. An excluded sub-node behaves in the context of the generation as-if it were never present in the Type under which it is.
In the both versions they can be excluded:
<excludes> <instanceDeclarations> <instanceDeclaration>namespace_uri:name_part_of_BrowseName</instanceDeclaration> </instanceDeclarations> </excludes>
For example, in order to exclude the PowerInput
subnode (that is used in ValveObjectType
in the SampleTypes model), you would use the following configuration:
<excludes> <instanceDeclarations> <instanceDeclaration>http://ua.prosysopc.com/SampleTypes:PowerInput</instanceDeclaration> </instanceDeclarations> </excludes>
Excluding a component does not remove it from the AddressSpace of the server, it just means that the generated accessor for it wont be available. It is possible to manually search nodes below nodes e.g. via UaNode.getComponent (for HasComponent reference targets), UaNode.getProperty (for HasProperty reference targets) and via UaNode.getReferences (and checking the target of the returned references). Excluded Methods do not create the handling logic, and must be done via a listener (see MyMethodManagerListener in the samples).
|
Currently the exclude excludes it from all types, as the main use case is to avoid name clashes with the SDK API. |
Starting from 5.1.0, Codegen will automatically exclude some typical BrowseNames (such as 'Description', 'DisplayName'; for every namespace) that would conflict with the base SDK API. |
4.3.5. Excluding output classes
This is an advanced option. One use-case for this is if you would e.g. only implement some of the methods for XXXTypeNode outputs, you might wish to include the implemented ones in source control and still generate the rest during build. |
If you need to exclude Java class from the outputs, you can use the following:
<excludes> <outputClasses> <outputClass>fully.qualified.java.name</outputClass> </outputClasses> </excludes>
This will only disable the writing of the file to the disk, the "generation" of it must still succeed. Also, the type can still be used in other types and must be present on the classpath. |
4.4. Sample configuration files
This is a basic example of a configuration file for the command line version. Note that the same file is available from the codegen/commandline/configurationexamples
folder
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<codegenConfiguration>
<!-- Mapping of UA Namespace -> Java package -->
<namespaceMappings>
<!-- Add mapping to your model(s) namespace(s) here -->
<!-- Each mapping must have unique java package name -->
<namespaceMapping>
<uri>http://ua.prosysopc.com/SampleTypes</uri>
<packageName>example.packagename</packageName>
</namespaceMapping>
</namespaceMappings>
<!-- Defines where models are loaded from. Use absolute paths, 'app.home'
is set by the launcher scripts to parent directory of the launch scripts
and can be used to make relative paths. Entries can be files or folders.
All xml files from the folder are assumed to be model files -->
<modelSources>
<modelSource>${app.home}/models</modelSource>
</modelSources>
<!-- These define which namespace should be generated and which targets
to use. See the Codegen Manual about the possible Generation Targets. -->
<generates>
<!-- Multiple generate blocks are possible -->
<generate>
<targets>all</targets>
<uris>
<!-- Multiple uris possible -->
<uri>http://ua.prosysopc.com/SampleTypes</uri>
</uris>
<outputs>
<code>${app.home}/sampletypes/output_code</code>
<resources>${app.home}/sampletypes/output_resources</resources>
</outputs>
</generate>
</generates>
<enhancements>
<!-- Optionally define a header to generate for each file. Every line is
prepended with '//'. -->
<fileHeader>
<line>Generated from SampleTypes</line>
<line>by Prosys OPC UA Java SDK Codegen</line>
</fileHeader>
</enhancements>
</codegenConfiguration>
Here is another example with the Maven plugin version. Please refer to the full pom.xml in the codegen/maven-integration/maven-integration-sample
. Please note that you must have taken the steps explained in the Using Codegen with Maven Integration section before the plugin can work.
<plugin>
<groupId>com.prosysopc.ua.codegen</groupId>
<artifactId>prosys-opc-ua-java-sdk-codegen-maven-plugin</artifactId>
<version>${codegen.plugin.version}</version>
<executions>
<execution>
<id>run-codegen</id>
<phase>generate-sources</phase>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<modelSources>
<modelSource>${project.basedir}/models</modelSource>
</modelSources>
<generates>
<generate>
<targets>all</targets>
<uris>
<uri>http://ua.prosysopc.com/SampleTypes</uri>
</uris>
<outputs>
<code>${codegen.output.code}</code>
<resources>${codegen.output.resources}</resources>
</outputs>
</generate>
</generates>
<namespaceMappings>
<namespaceMapping>
<uri>http://ua.prosysopc.com/SampleTypes</uri>
<packageName>example.packagename</packageName>
</namespaceMapping>
</namespaceMappings>
</configuration>
</execution>
</executions>
</plugin>
5. Using Generated Classes in Applications
5.1. Automatic Model Registration
The generated InformationModel classes must be registered to the SDK before they can be used in applications.
To avoid the manual registration via registerModel
(as described in the Client and Server Tutorial), you can use the client_model_provider
and server_model_provider
Generation Targets to skip this phase in your applications. Both create a special resource file to the resources output folder. If this is included in your application resources, SDK is able to locate and register the respective models automatically.
The SDK uses the standard Java ServiceLoader mechanism to locate the resources, so the generated files (com.prosysopc.ua.client.ClientCodegenModelProvider or com.prosysopc.ua.server.ServerCodegenModelProvider under META-INF/services ) must be visible in the Java classpath. This might not be the case by default, if you are using some environment that uses custom ClassLoaders, such as OSGi, so check the respective configuration, if it doesn’t seem to work for you. Additionally it should be stressed that the part from classpath root must be META-INF/services/com.prosysopc.ua.client.ClientCodegenModelProvider and META-INF/services/com.prosysopc.ua.client.ServerCodegenModelProvider for this to work.
|
These targets are special compared to other Codegen outputs, which replaces any existing files. In order for the above to work, Codegen instead appends to the files, if needed. This allows generating more than one namespace to one configured output (all other outputs will go to their respective Java packages). |
5.2. Client-side Applications
For instructions on using the generated classes in client-side applications developed with the Prosys OPC UA SDK for Java, please refer to the Prosys OPC UA SDK for Java Client Tutorial in the 'tutorial' folder of the distribution package.
5.3. Server-side Applications
For instructions on using the generated classes in server-side applications developed with the Prosys OPC UA SDK for Java, please refer to the Prosys OPC UA SDK for Java Server Tutorial in the 'tutorial' folder of the distribution package.
5.3.1. Method Implementations
Instead of defining method implementations in the actual classes, the SDK also supports implementing them out of the actual class. Therefore, the Codegen also generates a method interface which can be implemented and plugged into the actual class. Please see the generated classes to learn how this can be done.