Kettle5.x步骤组件开发总结

1. 前言

Kettle允许我们自定义开发插件以支持实际项目中特殊的流程,关于Kettle插件体系的介绍可以参考链接:Kettle插件体系介绍。这篇文章主要是针对Step插件的开发总结,不涉及开发其他类型插件的介绍。注意Kettle版本为5.x和4.x的插件结构稍有不同,本文针对的Kettle版本为5.x版本。

2. 开发总结

2.1. Step插件开发TODO

假设我们要实现一个Step插件,命名为Xxx,下面的类是必须实现的:

1
2
3
4
public class Xxx extends BaseStep implements StepInterface
public class XxxMeta extends BaseStepMeta implements StepMetaInterface
public class XxxData extends BaseStepData implements StepDataInterface
public class XxxDialog extends BaseStepDialog implements StepDialogInterface

【备注】
Kettle的插件体系已经相当完善,为了节省开发者自定义插件所花费的时间和精力,已经将大部分核心方法都实现并封装在各自的基类中,如BaseStep、BaseStepMeta、BaseStepDialog。如果想再进一步了解Kettle Step插件,可以阅读BaseXXX相关源码。

2.1.1. 实现Xxx

Step流程处理类,当Step真正运行起来,就是在这个实现类里面进行数据处理的,大部分核心方法都在BaseStep父类里面实现了,需要我们自行实现的方法有以下几个:

(1) processRow

步骤流程处理主方法,调用该方法来处理前面步骤输入的每一行数据,注意processRow每次处理一行数据。通过getRow()方法可以获取待处理的一行数据。当processRow方法返回true表示还有输入行要处理,当processRow返回false表示所有的行已经处理并可以结束对processRow函数的循环调用(在返回fasle之前通常都会调用setOutputDone来表述数据已经输出完毕)。

(2) init

步骤初始化的逻辑处理,可以在处理行数据之前做一些初始化逻辑操作,如打开文件流,打开网络连接,打开数据库连接等操作。如果没有特殊的初始化操作可以不用重写该方法。重写该方法记得先调用super.init(...,...)

1
2
3
4
5
@Override
public boolean init(StepMetaInterface smi, StepDataInterface sdi) {
super.init(smi, sdi);
// .... your extra init code
}

(3) dispose

步骤处理结束后的逻辑处理,可以在这里释放资源,如关闭文件流,关闭网络连接,关闭数据库连接等。重写改方法记得先调用super.dispose(...,...)

1
2
3
4
5
@Override
public void dispose(StepMetaInterface smi, StepDataInterface sdi) {
super.dispose(smi, sdi);
// .... your extra dispose code
}

2.1.2. 实现XxxMeta

实现这个类的作用有以下几点:

  • 负责Step流程元数据处理
  • 存储数据展示在对应的StepDialog上
  • 将核心数据保存在xml、资源库中,并可以从xml或者资源库读取并构成XxxMeta对象

大部分核心方法都已经在BaseStepMeta父类中实现了,其中需要我们重写实现的方法有以下几个:

(1) getDialogClassName

该方法返回自定义XxxDialog的全限类名(包含包名),如果XxxStep所在的包名为com.A.di.trans.steps.xxx.XxxStep,那么默认如果不重写该方法返回的XxxDialog全限类名为com.A.di.ui.trans.steps.xxx.XxxDialog

(2) getStep

构造Xxx实例,eg:

1
2
3
4
5
6
@Override
public StepInterface getStep(StepMeta stepMeta,
StepDataInterface stepDataInterface, int copyNr,
TransMeta transMeta, Trans trans) {
return new IdCardValidator(stepMeta, stepDataInterface, copyNr, transMeta, trans);
}

(3) getStepData

构造XxxData实例,eg:

1
2
3
4
@Override
public StepDataInterface getStepData() {
return new IdCardValidatorData();
}

(4) getFields

处理输出行元数据,一般用于将输入行元数据添加额外的列字段元数据,这个方法通常在下一个步骤要用到上一个步骤输出的列字段时被调用。

如下所示添加额外的列字段到输出行,eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Override
public void getFields(RowMetaInterface inputRowMeta, String name,
RowMetaInterface[] info, StepMeta nextStep, VariableSpace space,
Repository repository, IMetaStore metaStore)
throws KettleStepException {
String realIsValidFieldName = space.environmentSubstitute(isValidFieldName);
if(!Const.isEmpty(realIsValidFieldName)) {
ValueMetaInterface v = new ValueMeta(realIsValidFieldName, ValueMeta.TYPE_BOOLEAN);
v.setOrigin(name); // set Name of the step to use as input for the origin field in the values
inputRowMeta.addValueMeta(v);
}
String realNotValidMsgFieldName = space.environmentSubstitute(notValidMsgFieldName);
if(!Const.isEmpty(realNotValidMsgFieldName)) {
ValueMetaInterface v = new ValueMeta(realNotValidMsgFieldName, ValueMeta.TYPE_STRING);
v.setOrigin(name);
inputRowMeta.addValueMeta(v);
}
String realIdCardLenFieldName = space.environmentSubstitute(idCardLenFieldName);
if(!Const.isEmpty(realIdCardLenFieldName)) {
ValueMetaInterface v = new ValueMeta(realIdCardLenFieldName, ValueMeta.TYPE_STRING);
v.setOrigin(name);
inputRowMeta.addValueMeta(v);
}
String realAreaCodeFieldName = space.environmentSubstitute(areaCodeFieldName);
if(!Const.isEmpty(realAreaCodeFieldName)) {
ValueMetaInterface v = new ValueMeta(realAreaCodeFieldName, ValueMeta.TYPE_STRING);
v.setOrigin(name);
inputRowMeta.addValueMeta(v);
}
String realBirthDateFieldName = space.environmentSubstitute(birthDateFieldName);
if(!Const.isEmpty(realBirthDateFieldName)) {
ValueMetaInterface v = new ValueMeta(realBirthDateFieldName, ValueMeta.TYPE_STRING);
v.setOrigin(name);
inputRowMeta.addValueMeta(v);
}
String realGenderFieldName = space.environmentSubstitute(genderFieldName);
if(!Const.isEmpty(realGenderFieldName)) {
ValueMetaInterface v = new ValueMeta(realGenderFieldName, ValueMeta.TYPE_STRING);
v.setOrigin(name);
inputRowMeta.addValueMeta(v);
}
}

(5) getXML

将XxxMeta需要保存的元数据以xml文本形式返回,在使用文件资源库并保存转换的时候会调用该方法将步骤元数据保存到xml文件中(ktr后缀)。

(6) loadXML

从xml节点中获取XxxMeta元数据,在导入ktr文件到Spoon界面的时候会调用该方法加载对应的元数据信息。

(7) saveRep

getXML方法类似,不同的是该方法将XxxMeta需要保存的元数据持久化到数据库资源库中

(8) readRep

从数据库资源库中读取XxxMeta元数据信息。

2.1.3. 实现XxxData

Step流程处理临时数据存储,实现该接口的类用来暂存流程处理过程所需要用到的数据,充当临时缓存。

2.1.4. 实现XxxDialog

Step插件元数据设置对话框,在Spoon中拖动该Step组件到设计区并双击时,就会调用这个实现类的open方法,实现该接口的类用来设置XxxMeta相关属性,其中需要我们实现的核心方法为:

(1) open

双击图标时候触发,负责渲染生成界面并添加XxxMeta元数据到界面上,这个方法会涉及到swt布局,所以方法体一般很长。方法返回值为步骤名stepname

2.1.5. 用注解代替plugin.xml

Kettle5.x版本可以用@Step注解定义来代替plugin.xml定义,@Step注解用于XxxMeta。关于@Step注解的使用说明可以参考以下链接:

kettle使用注解开发插件

@Step注解属性说明:

  • id —— 插件唯一标识
  • image —— 指定Step插件在Spoon中显示的图标
  • i18nPackageName —— 指定国际化messages所在的包名
  • name —— Step插件命名
  • description —— Step插件描述
  • categoryDescription —— 指定Step插件所属的Spoon转换”核心对象”目录

2.1.6. 国际化(可选)

国际化使Spoon界面展示的说明文字、日志信息能够适应不同国家语言。虽然国际化在插件开发里面是可选的,但是仍然建议每个插件开发时都使用国际化来增加插件的可用性和可扩展性。

实现国际化首先要在@Step注解中指定的i18nPackageName显示我们要存储国际化信息的基目录。然后在这个包下面新建多个properties,命名规则为messages_[locate].properties,其中locate表示国家,如en_US表示美国,zh_CN表示中国。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
IdCardValidator.TransName=IdCard Validator
IdCardValidator.TransDescription=validate id card format
IdCardValidatorDialog.DialogTitle=IdCard Validator
IdCardValidatorDialog.Stepname.Label=IdCard Validator
IdCardValidatorDialog.FieldName.Label=IdCard Field
IdCardValidatorDialog.OutputFields.Label=Output Fields
IdCardValidatorDialog.IsValid.Label=IsValid field
IdCardValidatorDialog.IsValid.Tooltip=choose one field to store the 'IsValid' result
IdCardValidatorDialog.IdCardLen.Label=IdCard Length field
IdCardValidatorDialog.IdCardLen.Tooltip=choose one field to store the 'IdCardLen' result
IdCardValidatorDialog.NotValidMsg.Label=NotValidMsg field
IdCardValidatorDialog.NotValidMsg.Tooltip=choose one field to store the 'NotValidMsg' result
IdCardValidatorDialog.AreaCode.Label=Area Code field
IdCardValidatorDialog.AreaCode.Tooltip=choose one field to store the 'AreaCode' result
IdCardValidatorDialog.BirthDate.Label=Birth Date field
IdCardValidatorDialog.BirthDate.Tooltip=choose one field to store the 'BirthDate' result
IdCardValidatorDialog.Gender.Label=Gender field
IdCardValidatorDialog.Gender.Tooltip=choose one field to store the 'Gender' result
IdCardValidatorDialog.FailedToGetFields.DialogTitle=FailToGetFields
IdCardValidatorDialog.FailedToGetFields.DialogMessage=fail to get fields from previous step
IdCardValidator.Error.IdCardFieldMissing=missing id card field
IdCardValidator.Exception.CoundNotFoundField=could not found the id card field
IdCardValidator.Error.IsValidFieldMissing=missing is valid field

在代码中通过如下方式来引用这些属性:

1
2
3
4
5
6
7
private static Class<?> PKG = IdCardValidatorMeta.class; // for i18n purposes, needed by Translator2!!
public String open() {
...
BaseMessages.getString( PKG, "IdCardValidatorDialog.DialogTitle" );
...
}
坚持原创技术分享,您的支持将鼓励我继续创作!