Creating a New DPU in UnifiedViews

This section contains a guide on how to create a DPU in UnifiedViews.

If you are not familiar with basic concepts of UnifiedViews (DPU, data unit, etc.), you find thebasic concepts of UnifiedViews in a separate section with respect to DPU development.

For general installation notes, refer to the installation guide.

Quick Start Using the DPU Archetype Template

Note

Before you start, make sure that the development environment is prepared as we described it in Development Environment Setup.

UnifiedViews provides a Maven Archetype that generates a skeleton for a new DPU for you.

You can access it in two ways:

  • From within your IDE, using the wizard. Click New ProjectMavenCreate from archetype

  • Use the interactive command line.

You have first to make sure that the SWC repository is configured correctly.

The Maven Archetype is either available at the remote UnifiedViews repository under the following coordinates:

  • groupId: eu.unifiedviews

  • artifactId: uv-dpu-template-base

  • version: matching the latest release of PoolParty, or your targeted release

Alternatively, you may also checkout and install the UnifiedViews - Plugin-devEnv repository, master branch on your computer to have all needed artifacts in your local repository.

Using the Command Line

If you are using IntelliJ as IDE, you can use IntelliJ's built-in terminal.

$ mvn archetype:generate -DarchetypeGroupId=eu.unifiedviews -DarchetypeArtifactId=uv-dpu-template-base -DarchetypeVersion=8.0.0 -Ddpu_name=my-dpu -Ddpu_type=Extractor -Ppoolparty-profile

Replace the values for dpu_name and dpu_type. For more information, go to basic concepts of UnifiedViews for supported DPU types, default is 'Transformer'.

The output should be similar to the following:

[INFO] Generating project in Interactive mode
...
Define value for property 'groupId': eu.unifiedviews.plugins
Define value for property 'artifactId': my-dpu-project
[INFO] Using property: version = 1.0.0-SNAPSHOT
Define value for property 'package' eu.unifiedviews.plugins: : 
[INFO] Using property: author = SWC
[INFO] Using property: dpu_name = my-dpu
[INFO] Using property: dpu_type = Extractor
[INFO] Using property: unifiedviews_api_version = 8.0.0
Confirm properties configuration:
groupId: eu.unifiedviews.plugins
artifactId: my-dpu-project
version: 1.0.0-SNAPSHOT
package: eu.unifiedviews.plugins
author: SWC
dpu_name: my-dpu
dpu_type: Extractor
unifiedviews_api_version: 8.0.0
 Y: : 
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: uv-dpu-template-base:8.0.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: eu.unifiedviews.plugins
[INFO] Parameter: artifactId, Value: my-dpu-project
[INFO] Parameter: version, Value: 1.0.0-SNAPSHOT
[INFO] Parameter: package, Value: eu.unifiedviews.plugins
[INFO] Parameter: packageInPathFormat, Value: eu/unifiedviews/plugins
[INFO] Parameter: dpu_name, Value: my-dpu
[INFO] Parameter: unifiedviews_api_version, Value: 8.0.0
[INFO] Parameter: dpu_type, Value: Extractor
[INFO] Parameter: author, Value: SWC
[INFO] Project created from Archetype in dir: /home/user/Develop/my-dpu-project
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Note

You will be asked for missing artifactId or groupId, but not for dpu_name and dpu_type, make sure to specify them on the command line.

Creating a New DPU Using IntelliJ

This section contains a short guide on how to create a DPU using the IntelliJ IDE.

  1. Open IntelliJ, create new project, select Maven.

  2. Check Create from archetype.

  3. Select the archetype with the ID as above. Click Next.

    • If such an archetype does not exist, just add it manually

      • Group ID: eu.unifiedviews,

      • Artifact ID: uv-dpu-template-base

      • Version: 8.0.0

  4. New project details. Fill in basic info about your DPU artifact which should be created for you:

    • GroupId.

      • Maven group ID for the prepared DPU artifact. In general, all your developed DPUs should use the same Maven Group ID.

      • For generic plugins use: eu.unifiedviews.plugins

      • For PP SWC plugins use: eu.unifiedviews.plugins.swc.poolparty

    • ArtifactId

      • Use pattern: uv-{first character from DPU type}-{dpuName in camelCase style }

        • so for example uv- t-myDpu to prepare DPU which is of type "Transformer" and has name "MyDpu"

    • Version (optional):

      • By default, the version of the DPU will be 1.0-SNAPSHOT. You may keep it as it is.

  5. Click next, add more properties of the DPU. To add a property, click '+ (see the screenshot below). The dialogue for adding a Maven property appears.

    Manually define the following properties:

    • dpu_type

      • Every DPU is of certain DPU type.

        Please specify one of the following values :

        • Extractor

        • Transformer (default)

        • Loader

        • Quality

    • dpu_name

      • Please specify the name of the DPU. This value is used as name of base DPU classes. Use only names which may be used as Java class names.

        • Suggested pattern CamelCase with big initial letter, e.g., MyDpu.

    • author

      • Name of the DPU author. This value is inserted into comments for all generated classes.

      • Example: 'Tomas Knap'

  6. Click Next, Fill project name, adjust project location to your needs:

    • Project name: {first character from DPU type}-{dpuName in camelCase style }

      • For example t-myDpu for the DPU above (not uv-t-myDpu)

    • Project location: Usually within the folder with plugins/SWC plugins.

  7. Click Finish.

Note

If the process ends with an error ('Cannot find the given archetype'), please ensure that the Maven repository at the location https://mvn.semantic-web.at/nexus/content/repositories/releases/ is used by Maven.

You may set up such remote repositories in Preferences, search for Maven, Repositories and add such remote repository. In case of difficulties, first checkout and install UnifiedViews - Plugin-devEnv repository, master branch

After the project has been generated, you may still need to change these settings:

  1. Mark the 'src' folder as containing source code files.

  2. Instruct Maven to automatically import projects.

  1. Right-click on the 'java' folder and select Mark directory as source root (see the blue folder below).

    24577499.png
  2. Instruct Maven to automatically import projects, in Preferences, search for Maven, select the Importing menu item and ensure that Import Maven projects automatically is checked.

    24577498.png

For further explanation of the generated files, please refer to: Explaining the Core DPU Files Generated by the DPU Template

You can also build the DPU (as it is) and try to import it to UnifiedViews' UI, see: Creating a New DPU Using IntelliJ

Creating a New DPU Using the Netbeans IDE

This section contains a short guide on how to use the Netbeans IDE for DPU development.

In order to set up the Netbeans IDE, follow these steps:

  1. Open the Netbeans IDE, Create new project, select Maven, Project from archetype.

    24577503.png
  2. Find the archetype named uv-dpu-template-base. Check that the version is the required version of UnifiedViews API. (Every release of UnifiedViews Core has also information about the used UnifiedViews API). Click Next.

    24577504.png
  3. Fill in basic information:

    • Project Name: this value is used as name of the project directory. Project name is also used as a Maven artifact ID, which is the unique Maven identifier of the DPU created.

      • Suggested pattern for project name: {first character from DPU type }-{dpuName in camelCase style}, so for example t-myDpu to prepare the DPU which is of type 'Transformer' and has the name 'MyDpu'.

    • Project Location: choose an arbitrary location of the project on your filesystem.

    • Artifact Id: automatically prefilled based on project name.

    • Group Id: Maven group ID for the prepared DPU. In general, all your developed DPUs should use the same Maven Group ID.

      • Suggested pattern: For company with homepage 'mycompany.eu', use 'eu.mycompany.uv.dpu'.

    • Version: You may keep as it is. So the initial version of the DPU will be 1.0-SNAPSHOT

    • Package: Java package in which the DPU classes will reside.

      • Suggested form of package name: {group id}.{type in lowercase}.{dpuName lowercase}

    • Additional Creation Properties:

      • dpu_type: Every DPU is of a certain DPU type. Please specify one of the following values:

        • Extractor

        • Transformer

        • Loader

        • Quality

      • unifiedviews_api_version: This is the version of the API, the DPU is built against. Do not change unless you know what are you doing.

      • author: Name of the DPU author. This value is inserted into comments for all generated classes.

      • dpu_name: Please specify the name of the DPU. This value is used as name of base DPU classes

        • Suggested pattern CamelCase with big initial letter, example, 'MyDpu'. Use only names which may be used as Java class names.

      24577505.png
  4. Check the prepared values. Click Finish. As a result, new project is prepared, with Core classes being already generated.

    24577506.png

For further explanation of the generated files, please see: Explaining the Core DPU Files Generated by the DPU Template

You can also build the DPU (as it is) and try to import it to UnifiedViews UI, see: Creating a New DPU Using the Netbeans IDE

Explaining the Core DPU Files Generated by the DPU Template

This section contains a short guide on files that are being created by the DPU archetype.

The previous section explained how the skeleton for a new DPU may be prepared.

This section describes the files generated by the DPU template. Let’s start with a short list to deliver the general idea, afterward more important files will be explained in detail.

Note

The discussion below assumes that you used the same value when setting up the creation of DPU from DPU template.

List of Generated Files
  • DPU source code files:

    • MyDpu.java - M ain DPU class. It contains the method 'innerExecute()' where all the business logic of the DPU goes and which is called when the DPU is executed.

    • MyDpuVaadinDialog - Vaadin configuration dialog. Every DPU can provide a Vaadin configuration dialog. The dialog is presented to pipeline designers in the frontend part of UnifiedViews, so that they can configure the DPU via a graphical interface.

    • MyDpuConfig_V1 - DPU's Configuration. The configuration class must have private fields with public getters/setters for each field. This class is used on the one hand by the main DPU class of the DPU (to configure the DPU as it is executed). On the other hand it used by Vaadin configuration dialog to configure the dialog or to store the configuration from the dialog.

  • DPU documentation files:

    • README.md

    • CHANGELOG.md

    • doc/About.md: English version of the user documentation

    • doc/About_{languageTag}.md (optional): here {languageTag} can be 'sk' for Slovak localization, for example.

  • DPU resource files for localization, localized messages, localized DPU's name:

    • src/main/resources/resources.properties: this file contains text that is presented to the user as a message or as a part of a configuration dialog. In our case the file contains two messages.

      Note

      It is highly recommended to put all the strings inside this file and not directly into code as it makes DPU localization much easier.

    • src/main/resources/resources_{languageTag}.properties, here, too, {languageTag} can be 'sk' for Slovak localization, for example.

DPU Source Code FilesMain DPU Class

Source code file, class: MyDpu.java, further referred to simply as 'MyDpu' class.

The generated MyDpu class looks as follows (imports are skipped):

@DPU.AsTransformer
public class MyDpu extends AbstractDpu<MyDpuConfig_V1> {
    private static final Logger LOG = LoggerFactory.getLogger(MyDpu.class);
                
    @ExtensionInitializer.Init
    public FaultTolerance faultTolerance;
        public MyDpu() {
                super(MyDpuVaadinDialog.class, ConfigHistory.noHistory(MyDpuConfig_V1.class));
        }
                
    @Override
    protected void innerExecute() throws DPUException {
        ContextUtils.sendShortInfo(ctx, "MyDpu.message");        
    }
}
  • Line 1 contains an annotation, which defines the DPU type, in our case it is a transformer. Other options are DPU.AsExtractor, DPU.AsLoader, DPU.asQuality, which corresponds with the available DPU types.

  • Line 2 shows that every DPU prepared should extend AbstractDpu class, base class for DPUs. The parameter of the AbstractDpu class is the configuration class, in this case MyDpuConfig_V1. The usage of configuration class as a template parameter ensures compatibility with the configuration dialog.

  • Line 3 defines the Logger object which should be used when the DPU developer wants to log certain information about the DPU's execution. Only reasonable size messages and reasonable amount of messages should be logged as all the logs are stored in the database.

  • Lines 10 - 12 contain the draft of the main execution method, which is called when the DPU is executed. This is the place where the DPU developer should put the business logic of the DPU.

AbstractDpu class, this DPU class inherits from, provides two important variables.

They can be used inside of the main execution method innerExecute():

  • config - points to DPU configuration - the instance of the MyDpuConfig_V1 class

  • ctx - holds instance of the execution context.

The innerExecute method can throw a DPUException.

This exception should be used in case the DPU developer would like to express failure of the DPU due to error in the DPU execution or due to cancellation of the DPU's execution. Some helpers and other classes also throw this exception. In general this exception is considered to be a non-recovery failure, which means, the DPU should fail.

  • In Line 14, the ContextUtils class is used to send an event. Events are another way of communication with the user, they present a high level overview of the DPU's execution. When compared with using Logger from Line 3, events should be send only in significant cases, so the rule of thumb is that every DPU should send just a couple of these events. For example, information about a number of input entries processed. Also another difference is if an ERROR level event is sent, then the DPU is considered to have failed. It finishes with ERROR.

    • In this case, in Line 11, an information message is send.

ContextUtils also provides other methods for message sending and a method for creation of DPUException.

Every string passed (besides args) to ContextUtils goes through the localization process. The second parameter of sendShortInfo() method in Line 11 actually points to the file with resources. Translations of the message, so the method sendShortInfo()automatically supports localization. For this reason, developers are encouraged to use ContextUtils for message sending and creation of DPUException.

Note

As a DPU developer you yourself must check for requests for cancellation of execution. This check should be implemented e.g. after/before every new entry (file, graph) is being processed by the DPU.

This can be done by using the following code fragment:

if (ctx.canceled()) {
    log.info("DPU execution was cancelled.");
    break;
}
Vaadin Configuration Dialog

Source code file, class: MyDpuVaadinDialog.java, further referred to simply as 'MyDpuVaadinDialog' class.

The generated MyDpuVaadinDialog class looks as follows, imports are skipped:

public class MyDpuVaadinDialog extends AbstractDialog<MyDpuConfig_V1> {

    public MyDpuVaadinDialog() {
        super(MyDpu.class);
    }
    
        @Override
    public void setConfiguration(MyDpuConfig_V1 c) throws DPUConfigException {
    }
    
        @Override
    public MyDpuConfig_V1 getConfiguration() throws DPUConfigException {
        final MyDpuConfig_V1 c = new MyDpuConfig_V1();
        return c;
    }
    
        @Override
    public void buildDialogLayout() {
        final VerticalLayout mainLayout = new VerticalLayout();
        mainLayout.setWidth("100%");
        mainLayout.setHeight("-1px");
        mainLayout.setMargin(true);
        
                mainLayout.addComponent(new Label(ctx.tr("MyDpu.dialog.label")));
        
                setCompositionRoot(mainLayout);
    }
}
  • Line 1 shows that the Vaadin configuration dialog class of the DPU always extends AbstractDialog class. We use the base dialog abstract class with our configuration as a template parameter. The template parameter ensures compatibility of configuration between Main DPU class and Vaadin configuration dialog class.

  • Lines 3 - 5 represent constructor for the Vaadin configuration class. In the constructor we need to pass the main DPU class to the AbstractDialog class. Thanks to this, AbstractDialog can scan the DPU class for configuration history and extensions.

  • In Lines 7- 9 the configuration dialog class is given a configuration object to load. So the DPU developer has to specify in this method how the values from the configuration object should be loaded to the Vaadin components. This method is called when the configuration dialog needs to be prepared for the pipeline designer.

  • In Lines 11 - 15, the DPU developer should specify how the values taken from the Vaadin components should be serialized to the configuration object. This is the last place where any validation of the configuration can be done. This method is called when the pipeline designer saves the configuration dialog. In case of invalid configuration DPUConfigException should be thrown by the DPU developer.

  • Lines 17 - 27 create Vaadin dialog layout. As specified in Line 20, the width of the dialog is set to '100%', so it takes the whole space in the horizontal level.

  • Line 21 specifies height to be ' -1px', which means that the layout takes only as much vertical space as needed.

  • In Line 24 the ctx.tr returns the value assigned to the given string MyDpu.dialog.label in resources.properties, thus, it returns the proper localized version of the label. If no value is presented in resource.properties, the string MyDpu.dialog.label is returned. It is highly recommended to externalize messages to resources.properties – out of the code.

    Additionally, Line 24 creates a Label with the text. The Label is a simple read-only Vaadin component for displaying text. Finally, mainLayout. addComponent() method in Line 24 adds the given component, the Label, to the layout. If we would not call this method, the Label would not be presented in the layout/dialog.

  • Line 26 sets the above defined layout mainLayout as the root layout of the dialog.

For more information about Vaadin component’s and layouts, please refer to the Vaadin homepage, where you can find the excellent Book of Vaadin.

DPU Configuration Class

Source code file, class: eu.mycompany.uv.dpu.transformer.mydpu.MyDpuConfig_V1.java, further referred to simply as 'MyDpuConfig_V' class.

The generated MyDpuConfig_V1 class looks as follows (imports are skipped):

public class MyDpuConfig_V1 {
    public MyDpuConfig_V1() {
    }
}

Note

As can be seen in Lines 1 - 4, this a normal Java class, which has non-parametric constructor. The configuration class must have private fields with public getters/setters for each configuration field. This class is used on one side by the main DPU class of the DPU (to configure the DPU as it is executed) and on the other side by the Vaadin configuration dialog to configure the dialog or to store the configuration from the dialog.

DPU Documentation Files:

The DPU template generates the following three files:

  • README.md: this file is the main file, which points to CHANGELOG.md file and user documentation files (doc/About.md). Further it includes technical documentation if needed.

  • CHANGELOG.md: this file holds the version history.

  • doc/About.md: this file contains the user documentation, which is also displayed to DPU users in UnifiedViews. Such user documentation may also be localized.

Note

UnifiedViews frontend shows the user documentation involved in doc/About.md - in the proper version based on the localization.

File README.md

The generated file looks as follows; the file follows Markdown syntax:

MyDpu
----------

### Documentation

* see [Plugin Documentation](./doc/About.md)

### Technical notes

* May be used instead of DPU X, it also replaces DPY Y.

### Version history

* see [Changelog](./CHANGELOG.md)
  • In Line 6, README file contains links to the user documentation (About.md, see below). Line 14 contains links to Changelog file (see below). Further, if you would like to specify certain technical notes, you may specify them on Line 10.

File CHANGELOG.md

The generated file looks as follows; the file follows Markdown syntax:

MyDpu
----------

v1.0.0-SNAPSHOT
---
* Initial version of the DPU
  • Lines 4-6 contains the description of the initial version.

Note

Whenever you change the version in the future, you should also specify the new version and features included in such version here.

Basically for each new version you have to add the following fragment to the start:

v1.1
---
* Description of the bugfix/feature 1
* Description of the bugfix/feature 2

File doc/About.md

The content of the About.md file contains the user documentation. As the DPU is built, it is converted to About.html file, which is then displayed in the UnifiedViews frontend in the DPU detail as user documentation.

The file generated by the template looks as follows; the file follows the Markdown syntax:

### Description

Description of the DPU intended for the user of the DPU. 

### Configuration parameters

| Name | Description |
|:----|:----|
|**Param** | Param which ...|

### Inputs and outputs

|Name |Type | DataUnit | Description | Mandatory |
|:--------|:------:|:------:|:-------------|:---------------------:|
|input |i |FilesDataUnit |Input files |x|
|output |o |RdfDataUnit |Produced RDF data |x|
  • Line 3 contains description of the DPU, which is then depicted to user. This should be quite short, but should explain to nontechnical users what the DPU does.

  • Lines 5-10 contain description of parameters which are available in Vaadin configuration dialog.

  • Lines 11-16 contains description of input/output data units which the DPU uses. Every data unit contains its name (as in Main DPU class), type (i = it is input data unit, o = it is output data unit), dataUnit (FilesDataUnit for files data unit, RdfDataUnit for rdf data unit), description and flag, whether it is mandatory or not (if it is mandatory, pipeline designer has to use such input/output data unit).

A sample file converted to HTML looks as follows:

24577650.png

If you want to support also user documentation in a language other than English, you can create more such user documentation files and store them as doc/About_{languageTag}.md, where {languageTag} is for example 'sk' for Slovak localization.

UnifiedViews automatically ensures that the proper user documentation is loaded based on the localization of the whole application.

DPU Resource Files for Localization - Localized Messages, Localized DPU Name

The file generated by the template looks as follows, it contains key=value pairs:

# Contains localization of used string in dialog and events. To avoid property collision all values
# must be prefixed by '{dpu name}.' for DPU execution and '{dpu name}.dialog' for configuration dialog!
# Additional prefix in form of dpu name is optional.

# DPU execution.
MyDpu.message = DPU is running ...

# DPU's dialog.
MyDpu.dialog.label = DPU's configuration

  • The generated file contains key-value pairs holding localized versions of strings used by the DPU .

  • You have to prefix message identifiers (keys) with ‘{dpu name}.’ or ‘{dpu name}.dialog’ based on their usage during DPU execution or in dialog respectively.

  • The generated file should contain English strings.

  • If you want to support also localizations in other languages than English, you may create more of such resource files and store them as resource_{languageTag}.properties, where {languageTag} is for example 'sk' for Slovak localization. UnifiedViews automatically ensures that the proper messages are used in the DPUs based on the localization of the whole application.

Create Pure JavaScript Dialogues

This section contains a short guide on how to develop JavaScript dialogues based on the Vaadin Framework.

Basic idea is described in https://vaadin.com/blog/vaadin-7-loves-javascript-components

Step 1: Define server part of your JavaScript component (see SparqlEditorComponent for more details).

package eu.unifiedviews.plugins.transformer.sparql.construct.editor;
 
@JavaScript({"vaadin://js/sparqleditor/sparqleditor.js", "vaadin://js/sparqleditor/jquery-v1.11.1.min.js", "vaadin://js/sparqleditor/yasqe.bundled.min.js"})
public class SparqlEditorComponent extends AbstractJavaScriptComponent {
 
        ...
        @Override
        protected SparqlEditorState getState() {
        return (SparqlEditorState) super.getState();
        }
 
}
  • @JavaScript annotation

  • extends AbstractJavaScriptComponent

  • override getState() method (see below)

Step 2: Prepare class holding the state object being exchanged between JS part of the component and server part.

In the example below, it just exchanges one string variable.

public class SparqlEditorState extends JavaScriptComponentState {
    public String query = "";
}

Note

Unfortunately, a component's state can't be modified on the client side, to be updated on the server side. (https://vaadin.com/blog/vaadin-7-loves-javascript-components)

Any possible getters and setters in a state can not be used on the JavaScript side. The values are accessed directly as properties. This is true even if you explicitly have written the accessors in your state class.

There is a way how to define new callbacks for JavaScript in Java (to e.g. report status change back to the server): addFunction on the SparqlEditorComponent

Step 3: Prepare JS part - which has to define the controller, which is bind to the server part component based on its full name (including package).

So in the example above the following would be done:

eu_unifiedviews_plugins_transformer_sparql_construct_editor_SparqlEditorComponent = function() {

    // Create the component
    var mycomponent = new mylibrary.MyComponent(this.getElement());

    // Handle changes from the server-side - whenewher the query is changed, this function is called
    this.onStateChange = function() {
        //access the state object and get the value for query via this.getState().query;
    };

}
  • The function name must reflect the name and package of the server part.

Dpu Using Sparqleditor + Value Change Listeners
sparqlEditorComponent = new SparqlEditorComponent();
sparqlEditorComponent.setStyleName("SparqlEditor");
sparqlEditorComponent.setSizeFull();
sparqlEditorComponent.setImmediate(true);


// Process a value input by the user from the client-side
sparqlEditorComponent.addValueChangeListener(
        new SparqlEditorComponent.CustomValueChangeListener() {
            @Override
            public void valueChange() {
                ...
            }
        });
SparqlEditorComponent

(Server part - value change listeners)

public interface CustomValueChangeListener extends Serializable {
    void valueChange();
}

List<CustomValueChangeListener> listeners = new ArrayList<CustomValueChangeListener>();

public void addValueChangeListener(CustomValueChangeListener listener) {
    listeners.add(listener);
}

References:

https://vaadin.com/blog/vaadin-7-loves-javascript-components

https://dzone.com/articles/integrating-html-and-0

https://vaadin.com/docs/v8/framework/gwt/gwt-javascript.html#gwt.javascript.example

JS to JAVA: https://vaadin.com/forum#!/thread/4690491

https://vaadin.com/api/com/vaadin/ui/AbstractJavaScriptComponent.html

Localization for DPU Components

This section contains a short guide on how to enable localization for DPU components. The file resources.properties in this location src/main/resources/resources.properties takes care of the localization for any DPU component present.Labels in dialogues, options, messages, and exceptions will be extended with the text contained in it. This file contains text that is presented to the user as a message or as a part of a configuration dialogue. In our case the file contains two messages.

Note

It’s highly recommended to put all the strings inside this file and not directly into code as it makes DPU localization much easier.

In order to properly support localization, each string that should be localized shall go through the localization function presented under user context ctx.tr. There might be some exceptions in helpers, for example ContextUtils call this function on every given part of messages.Text in DPUs should be denoted as a name of properties in the resource.properties file. See existing DPUs for examples. It’s highly recommended to prefix each string with DPU name.

DPU Dialogues - Styling of DPUs with PoolParty

Basic Layout With One Text Field:
@Override
public void buildDialogLayout() {

        final VerticalLayout mainLayout = new VerticalLayout();
        mainLayout.setWidth("100%");
        mainLayout.setMargin(true);
        mainLayout.setSpacing(true);

        mainLayout.addComponent(new Label(ctx.tr("MyDpu.dialog.label")));

        tfConfigSample = new TextField(ctx.tr("MyDpu.dialog.tfSample.caption"));
        tfConfigSample.setWidth("100%");
        tfConfigSample.setNullRepresentation("");
        tfConfigSample.setRequired(false);
        tfConfigSample.setImmediate(true);
        mainLayout.addComponent(tfConfigSample); 

    setCompositionRoot(mainLayout);
}
Validation of a Textbox - Simple

A value is required. When the configuration is saved, it displays a custom error. This does not provide inline validation.

@Override
public void buildDialogLayout() {
    final VerticalLayout mainLayout = new VerticalLayout();
    mainLayout.setWidth("100%");
    mainLayout.setMargin(true);
    mainLayout.setSpacing(true);

    mainLayout.addComponent(new Label(ctx.tr("MyDpu.dialog.label")));

    tfConfigSample = new TextField(ctx.tr("MyDpu.dialog.tfSample.caption"));
    tfConfigSample.setWidth("100%");
    tfConfigSample.setNullRepresentation("");
    tfConfigSample.setRequired(true);
        tfConfigSample.setImmediate(true);
    mainLayout.addComponent(tfConfigSample);

    setCompositionRoot(mainLayout);
}


 @Override
    public MyDpuConfig_V1 getConfiguration() throws DPUConfigException {
        final MyDpuConfig_V1 c = new MyDpuConfig_V1();

                if (!tfConfigSample.isValid()) {
            throw new DPUConfigException(ctx.tr("MyDpu.dialog.label.notValid"));
        }

        c.setConfigSample(tfConfigSample.getValue());
        return c;
    }


Validation of a Textbox - Custom

When the configuration is saved, it displays a custom error. Also it provides inline validation, as the user is editing the configuration

@Override
public void buildDialogLayout() {
    final VerticalLayout mainLayout = new VerticalLayout();
    mainLayout.setWidth("100%");
    mainLayout.setMargin(true);
    mainLayout.setSpacing(true);

    mainLayout.addComponent(new Label(ctx.tr("MyDpu.dialog.label")));

    tfConfigSample = new TextField(ctx.tr("MyDpu.dialog.tfSample.caption"));
    tfConfigSample.setWidth("100%");
    tfConfigSample.setNullRepresentation("");
    tfConfigSample.setRequired(true);
        tfConfigSample.setImmediate(true);
    tfConfigSample.addValidator(value -> {
        if (tfConfigSample.getValue().isEmpty() || tfConfigSample.getValue().length() < 3) {
            throw new Validator.InvalidValueException(ctx.tr("MyDpu.dialog.label.notValid"));
        }
    });
    mainLayout.addComponent(tfConfigSample);

    setCompositionRoot(mainLayout);
}


 @Override
    public MyDpuConfig_V1 getConfiguration() throws DPUConfigException {
        final MyDpuConfig_V1 c = new MyDpuConfig_V1();

        try {
                tfConfigSample.validate();
                } catch (Validator.InvalidValueException e) {
                throw new DPUConfigException(e.getMessage(), e);
                }

        c.setConfigSample(tfConfigSample.getValue());
        return c;
    }


Combo Box With NO Option to Add New Items
ComboBox cbMode = new ComboBox(ctx.tr("BatchLinkingDbpedia.dialog.processingmode.caption"));
cbMode.addItem(BatchLinkingDbpediaConfig_V1.Mode.OPTIMISTIC);
cbMode.setItemCaption(BatchLinkingDbpediaConfig_V1.Mode.OPTIMISTIC, ctx.tr("BatchLinkingDbpedia.dialog.processingmode.optimistic"));
cbMode.addItem(BatchLinkingDbpediaConfig_V1.Mode.CONSERVATIVE);
cbMode.setItemCaption(BatchLinkingDbpediaConfig_V1.Mode.CONSERVATIVE, ctx.tr("BatchLinkingDbpedia.dialog.processingmode.conservative"));
cbMode.setInvalidAllowed(false);
cbMode.setNullSelectionAllowed(false);
cbMode.setImmediate(true);
mainLayout.addComponent(cbMode);


# Set value to config object
c.setLinkingMode((BatchLinkingDbpediaConfig_V1.Mode)cbMode.getValue());


# Get value from config object
cbMode.setValue(c.getLinkingMode());
Combo Box With the Ability to Insert New Items

This concerns items, which are not simple strings.

See the code below, important is the 'cbTypeOfLinkProduced.setNewItemsAllowed(true);' option, plus 'newItemHandler' to process new entries (if they are not just simple strings)

ComboBox cbTypeOfLinkProduced  = new ComboBox(ctx.tr("BatchLinkingDbpedia.dialog.linkprooduced.caption"));
cbTypeOfLinkProduced.setInvalidAllowed(false);
cbTypeOfLinkProduced.setNullSelectionAllowed(false);
cbTypeOfLinkProduced.setNewItemsAllowed(true);
cbTypeOfLinkProduced.setTextInputAllowed(true);
cbTypeOfLinkProduced.setImmediate(true);
cbTypeOfLinkProduced.addValidator(value -> {
    if (!isValidIri(value)) {
        throw new Validator.InvalidValueException(ctx.tr("BatchLinkingDbpedia.dialog.linkprooduced.invalid"));
    }
});
// Custom handling for new items
cbTypeOfLinkProduced.setNewItemHandler(new AbstractSelect.NewItemHandler() {
    @Override
    public void addNewItem(String newItemCaption) {
        try {
                        IRI newLink = VF.createIRI(newItemCaption);
                        cbTypeOfLinkProduced.addItem(newLink);

                        // Remember to set the selection to the new item
                        cbTypeOfLinkProduced.select(newLink);            
        } catch (Exception e) {
            throw new Validator.InvalidValueException(ctx.tr("BatchLinkingDbpedia.dialog.linkprooduced.invalid"));
        }
    }
});
mainLayout.addComponent(cbTypeOfLinkProduced);


# Saving the configuration to the config object - in "getConfiguration" method:
cbTypeOfLinkProduced.validate();
c.setLinkUri((IRI)cbTypeOfLinkProduced.getValue());


# Reading the configuration from the config object - in "setConfiguration" method:
comboBox.addItem(SAMEAS);
..
comboBox.addItem(c.getLinkUri());
comboBox.select(c.getLinkUri());
Create New Button

When creating a new button, use the following additional styles:

button.addStyleName("Button Button--sm Button--primary");