Components are configured using their constructor parameters. They can all be marked with the @Option
property, which lets you give a name to parameters.
For the name to be correct, you must follow these guidelines:
-
Use a valid Java name.
-
Do not include any
.
character in it. -
Do not start the name with a
$
. -
Defining a name is optional. If you don’t set a specific name, it defaults to the bytecode name, which can require you to compile with a
-parameter
flag to not end up with names such asarg0
,arg1
, and so on.
Parameter types can be primitives or complex objects with fields decorated with @Option
exactly like method parameters.
It is recommended to use simple models which can be serialized in order to ease serialized component implementations. |
For example:
class FileFormat implements Serializable {
@Option("type")
private FileType type = FileType.CSV;
@Option("max-records")
private int maxRecords = 1024;
}
@PartitionMapper(family = "demo", name = "file-reader")
public MyFileReader(@Option("file-path") final File file,
@Option("file-format") final FileFormat format) {
// ...
}
Using this kind of API makes the configuration extensible and component-oriented, which allows you to define all you need.
The instantiation of the parameters is done from the properties passed to the component.
Examples of option names:
Option name | Valid |
---|---|
myName |
|
my_name |
|
my.name |
|
$myName |
Primitives
A primitive is a class which can be directly converted from a String
to the expected type.
It includes all Java primitives, like the String
type itself, but also all types with a org.apache.xbean.propertyeditor.Converter
:
-
BigDecimal
-
BigInteger
-
File
-
InetAddress
-
ObjectName
-
URI
-
URL
-
Pattern
Complex object mapping
The conversion from property to object uses the Dot notation.
For example, assuming the method parameter was configured with @Option("file")
:
file.path = /home/user/input.csv
file.format = CSV
matches
public class FileOptions {
@Option("path")
private File path;
@Option("format")
private Format format;
}
List case
Lists rely on an indexed syntax to define their elements.
For example, assuming that the list parameter is named files
and that the elements are of the FileOptions
type, you can define a list of two elements as follows:
files[0].path = /home/user/input1.csv
files[0].format = CSV
files[1].path = /home/user/input2.xml
files[1].format = EXCEL
Map case
Similarly to the list case, the map uses .key[index]
and .value[index]
to represent its keys and values:
// Map<String, FileOptions>
files.key[0] = first-file
files.value[0].path = /home/user/input1.csv
files.value[0].type = CSV
files.key[1] = second-file
files.value[1].path = /home/user/input2.xml
files.value[1].type = EXCEL
// Map<FileOptions, String>
files.key[0].path = /home/user/input1.csv
files.key[0].type = CSV
files.value[0] = first-file
files.key[1].path = /home/user/input2.xml
files.key[1].type = EXCEL
files.value[1] = second-file
Avoid using the Map type. For example, if you can configure your component with an object instead. |
Defining Constraints and validations on the configuration
You can use metadata to specify that a field is required or has a minimum size, and so on. This is done using the validation
metadata in the org.talend.sdk.component.api.configuration.constraint
package:
API | Name | Parameter Type | Description | Supported Types | Metadata sample |
---|---|---|---|---|---|
@org.talend.sdk.component.api.configuration.constraint.Max |
maxLength |
double |
Ensure the decorated option size is validated with a higher bound. |
CharSequence |
{"validation::maxLength":"12.34"} |
@org.talend.sdk.component.api.configuration.constraint.Min |
minLength |
double |
Ensure the decorated option size is validated with a lower bound. |
CharSequence |
{"validation::minLength":"12.34"} |
@org.talend.sdk.component.api.configuration.constraint.Pattern |
pattern |
string |
Validate the decorated string with a javascript pattern (even into the Studio). |
CharSequence |
{"validation::pattern":"test"} |
@org.talend.sdk.component.api.configuration.constraint.Max |
max |
double |
Ensure the decorated option size is validated with a higher bound. |
Number, int, short, byte, long, double, float |
{"validation::max":"12.34"} |
@org.talend.sdk.component.api.configuration.constraint.Min |
min |
double |
Ensure the decorated option size is validated with a lower bound. |
Number, int, short, byte, long, double, float |
{"validation::min":"12.34"} |
@org.talend.sdk.component.api.configuration.constraint.Required |
required |
- |
Mark the field as being mandatory. |
Object |
{"validation::required":"true"} |
@org.talend.sdk.component.api.configuration.constraint.Max |
maxItems |
double |
Ensure the decorated option size is validated with a higher bound. |
Collection |
{"validation::maxItems":"12.34"} |
@org.talend.sdk.component.api.configuration.constraint.Min |
minItems |
double |
Ensure the decorated option size is validated with a lower bound. |
Collection |
{"validation::minItems":"12.34"} |
@org.talend.sdk.component.api.configuration.constraint.Uniques |
uniqueItems |
- |
Ensure the elements of the collection must be distinct (kind of set). |
Collection |
{"validation::uniqueItems":"true"} |
When using the programmatic API, metadata is prefixed by tcomp:: . This prefix is stripped in the web for convenience, and the table above uses the web keys.
|
Also note that these validations are executed before the runtime is started
(when loading the component instance) and that the execution will fail if they don’t pass.
If somehow it breaks your application you can disable that validation on the JVM by setting the system property talend.component.configuration.validation.skip
to true
.
Marking a configuration as a particular type of data
It is common to classify the incoming data. It is similar to tagging data with several types. Data can commonly be categorized as follows:
-
Datastore: The data you need to connect to the backend.
-
Dataset: A datastore coupled with the data you need to execute an action.
API | Type | Description | Metadata sample |
---|---|---|---|
o.t.s.c..api.configuration.type.DataSet |
dataset |
Mark a model (complex object) as being a dataset. |
{"tcomp::configurationtype::type":"dataset","tcomp::configurationtype::name":"test"} |
o.t.s.c..api.configuration.type.DataStore |
datastore |
Mark a model (complex object) as being a datastore (connection to a backend). |
{"tcomp::configurationtype::type":"datastore","tcomp::configurationtype::name":"test"} |
The component family associated with a configuration type (datastore/dataset) is always the one related to the component using that configuration. |
Those configuration types can be composed to provide one configuration item. For example, a dataset type often needs a datastore type to be provided. A datastore type (that provides the connection information) is used to create a dataset type.
Those configuration types are also used at design time to create shared configurations that can be stored and used at runtime.
For example, in the case of a relational database that supports JDBC:
-
A datastore can be made of:
-
a JDBC URL
-
a username
-
a password.
-
-
A dataset can be made of:
-
a datastore (that provides the data required to connect to the database)
-
a table name
-
data.
-
The component server scans all configuration types and returns a configuration type index. This index can be used for the integration into the targeted platforms (Studio, web applications, and so on).
The configuration type index is represented as a flat tree that contains all the configuration types, which themselves are represented as nodes and indexed by ID.
Every node can point to other nodes. This relation is represented as an array of edges that provides the child IDs.
As an illustration, a configuration type index for the example above can be defined as follows:
{nodes: {
"idForDstore": { datastore:"datastore data", edges:[id:"idForDset"] },
"idForDset": { dataset:"dataset data" }
}
}
Defining links between properties
If you need to define a binding between properties, you can use a set of annotations:
API | Name | Description | Metadata Sample |
---|---|---|---|
@o.t.s.c..api.configuration.condition.ActiveIf |
if |
If the evaluation of the element at the location matches value then the element is considered active, otherwise it is deactivated. |
{"condition::if::target":"test","condition::if::value":"value1,value2"} |
@o.t.s.c..api.configuration.condition.ActiveIfs |
ifs |
Allows to set multiple visibility conditions on the same property. |
{"condition::if::value::0":"value1,value2","condition::if::value::1":"SELECTED","condition::if::target::0":"sibling1","condition::if::target::1":"../../other"} |
The target element location is specified as a relative path to the current location, using Unix path characters.
The configuration class delimiter is /
.
The parent configuration class is specified by ..
.
Thus, ../targetProperty
denotes a property, which is located in the parent configuration class and is named targetProperty
.
When using the programmatic API, metadata is prefixed with tcomp:: . This prefix is stripped in the web for convenience, and the previous table uses the web keys.
|
Adding hints about the rendering based on configuration/component knowledge
In some cases, you may need to add metadata about the configuration to let the UI render that configuration properly.
For example, a password value that must be hidden and not a simple clear input box. For these cases - if you want to change the UI rendering - you can use a particular set of annotations:
API | Description | Generated property metadata |
---|---|---|
@o.t.s.c..api.configuration.ui.DefaultValue |
Provide a default value the UI can use - only for primitive fields. |
{"ui::defaultvalue::value":"test"} |
@o.t.s.c..api.configuration.ui.OptionsOrder |
Allows to sort a class properties. |
{"ui::optionsorder::value":"value1,value2"} |
@o.t.s.c..api.configuration.ui.layout.AutoLayout |
Request the rendered to do what it thinks is best. |
{"ui::autolayout":"true"} |
@o.t.s.c..api.configuration.ui.layout.GridLayout |
Advanced layout to place properties by row, this is exclusive with |
{"ui::gridlayout::value1::value":"first|second,third","ui::gridlayout::value2::value":"first|second,third"} |
@o.t.s.c..api.configuration.ui.layout.GridLayouts |
Allow to configure multiple grid layouts on the same class, qualified with a classifier (name) |
{"ui::gridlayout::Advanced::value":"another","ui::gridlayout::Main::value":"first|second,third"} |
@o.t.s.c..api.configuration.ui.layout.HorizontalLayout |
Put on a configuration class it notifies the UI an horizontal layout is preferred. |
{"ui::horizontallayout":"true"} |
@o.t.s.c..api.configuration.ui.layout.VerticalLayout |
Put on a configuration class it notifies the UI a vertical layout is preferred. |
{"ui::verticallayout":"true"} |
@o.t.s.c..api.configuration.ui.widget.Code |
Mark a field as being represented by some code widget (vs textarea for instance). |
{"ui::code::value":"test"} |
@o.t.s.c..api.configuration.ui.widget.Credential |
Mark a field as being a credential. It is typically used to hide the value in the UI. |
{"ui::credential":"true"} |
@o.t.s.c..api.configuration.ui.widget.Structure |
Mark a List<String> or Map<String, String> field as being represented as the component data selector (field names generally or field names as key and type as value). |
{"ui::structure::type":"null","ui::structure::discoverSchema":"test","ui::structure::value":"test"} |
@o.t.s.c..api.configuration.ui.widget.TextArea |
Mark a field as being represented by a textarea(multiline text input). |
{"ui::textarea":"true"} |
When using the programmatic API, metadata is prefixed with tcomp:: . This prefix is stripped in the web for convenience, and the previous table uses the web keys.
|
Target support should cover org.talend.core.model.process.EParameterFieldType
but you need to ensure that the web renderer is able to handle the same widgets.