- PVSM.RU - https://www.pvsm.ru -
Возможности ETL средства IBM DataStage покрывают достаточно широкий спектр требований, которые возникают в задачах по интеграции данных, но, рано или поздно, возникает потребность расширить функциональные возможности, внедряя Parallel Routines на языке С или создавая Java классы, которые, в дальнейшем используются в Java Transformer или Java Client. Довольно ограниченные возможности же встроенного языка Basic давно устарели и не могут расцениваться как серьезное подспорье (так, например, невозможно использовать XML структуры, или, другой пример — попробуйте написать хеширование MD5 при помощи Basic. Это возможно, но займет значительное время на разработку и отладку).
Как бы там ни было, хотелось бы иметь достаточно гибкое средство, позволяющее работать с потоком данных, не требующее перекомпиляции своих исходных кодов и которое можно было бы использовать в редакторе DataStage Client. Моим коллегой и близким другом было предложено разработать Groovy Transformer. О нем и пойдет речь в данной заметке.
Почему Groovy? Потому что этот язык достаточно гибок и обладает всеми возможностями Java, так как является надстройкой над этим языком, но помимо этого он предлагает разработчикам следующие преимущества:
import java.util.* ;
…
HashMap<String,String> someMap=new HashMap<String,String>() ;
someMap.put("Key1", "Value") ;
…
String valFromMap=someMap.get("Key1") ;
можно использовать следующий код Groovy (java.util.* в нем уже импортирована по умолчанию):
HashMap someMap=new HashMap();
someMap.Key1= "Value" ;
…
valFromMap=someMap.Key1;
def NumberOfItems= 10;
println "Number of items=$NumberOfItems";
def GroovyCode='sum=a+b; println "Sum is $sum"';
Binding binding=new Binding();
binding.setVariable("a", 10);
binding.setVariable("b", 20);
GroovyShell shell = new GroovyShell(binding);
shell.evaluate(GroovyCode);
def writer=new StringWriter() ;
def xmlOut=new groovy.xml.MarkupBuilder(writer) ;
xmlOut.JobsInfo{
Job(name : 'ODS_MOUVEMENT_C') {
precedants {
precedant "ODS_ECRITURE_C"
precedant "ODS_TEC_DEB"
}
}
}
println writer;
Этот пример должен распечатать следующий XML:
<JobsInfo>
<Job name='ODS_MOUVEMENT_C'>
<precedants>
<precedant>ODS_ECRITURE_C</precedant>
<precedant>ODS_TEC_DEB</precedant>
</precedants>
</Job>
</JobsInfo>
если вы хотите сгенерировать JSON, нужно заменить groovy.xml.MarkupBuilder на groovy.json.JsonBuilder.
Основная идея Groovy Transformer заключается в использовании кода на языке Groovy в Java Transformer. Поскольку код можно писать прямо в этом стейдже, то вы можете сами решать, как вам поступать – выполнять Groovy-код, который хранится в файле, приходит из параметров джоба или который вы напишете сами.
Таким образом, нам надо научиться создавать Java Transformer. Те, кто уже знают, как это реализуется, может пропустить этот раздел. Но я постараюсь кратко, так как документация по этой части написана достаточно подробно.
Итак, чтобы создать Ява-трансформер, нам нужно создать класс, который наследуется от класса Stage:
import com.ascentialsoftware.jds.* ;
class MyJavaTransformer extends Stage{
}
И необходимо имплементировать три самых часто используемых метода: initialize(), process() и terminate().
Метод initialize() выполняется перед обработкой стейджем строк потока и может содержать объявления объектов, которые вы собираетесь использовать в течении всей жизни трансформера.
Метод process() выполняется для каждой линии входного потока и должен содержать логику вашей обработки.
Метод terminate() выполняется в конце существования трансформера и в нем могут содержаться действия для удаления временных объектов (да знаю я, что нет деструкторов в Яве, имеется в виду любой мусор, который вы использовали: файлы, таблицы, мало ли что).
Замечание для параллельного режима трансформера: DataStage запускает отдельную Яву-машину на каждую ноду. Другими словамИ, если у вас четыре ноды, то DataStage запустит четыре JVM. Так как виртуальные машины изолированы, то у вас нет приемлемых способов обмена данными, между потоками, запущенными в каждой из них.
Теперь мы готовы к созданию шаблону нашего Ява-трансформера:
import com.ascentialsoftware.jds.*;
public class MyJavaTransformer extends Stage {
public void initialize() {
trace("Init");
}
public void terminate() {
trace("Terminate");
}
public int process() {
return 0;
}
}
Для чтения входящих в трансформер строк и выходящих из него, можно использовать объект Row и два метода: readRow() для доступа к значениям входного потока и writeRow() для записи в выходной.
Объекь Row также дает возможность получить метаданные каждой колонки и позволяет получить значения этих колонок. Следующий пример демонстрирует, каким образом можно заменить значения всех колонок, которые имеют тип VarChar значением « Hello from the Java », все остальные колонки «проталкиваются» без изменений дальше:
public int process() {
Row inputRow=readRow() ;
if (inputRow == null)
//нет больше строк в потоке
return OUTPUT_STATUS_END_OF_DATA;
Row outputRow=createOutputRow();
for (int i=0;i<inputRow.getColumnCount();i++) {
Object column=inputRow.getValueAsSQLTyped(i);
if (column instanceof java.lang.String)
outputRow.setValueAsSQLTyped(i, “Hello from Java”);
else
outputRow.setValueAsSQLTyped(i, column);
}
writeRow(outputRow);
}
Заметка: Чтобы скомпилировать класс трансформера, не забудьте импортировать библиотеку tr4j.jar в class path или в вашей IDE.
Теперь мы можем сформулировать требования к нашему Groovy Transformer.
Groovy Transformer- это JavaTransformer который компилирует на лету код Groovy. В него добавлен синтаксический сахар для облегчения рутинных операций, которые приходится выполнять при работе с классом Stage
Итак, наш трансформер должен:
В соответствии с этим требованиями, был создан Groovy Transformer, который вы можете скачать здесь: http://geckelberryfinn.ru/fr/GroovyTransformer.html [1]. (Осторожно! Этот Java Transformer также написан на Groovy =), с декомпиляцией будут проблемы).
Groovy Transformer предопределяет следующие объекты:
| Объект | Описание | Пример |
|---|---|---|
| GTransformer | Object. Ссылка на this класса Stage. Содержит все методы и атрибуты этого класса | GTransformer.createOutputRow() |
| OutputMatching | HashMap. Содержит соответствие имен колонок и их индексов. | OutputMatching.get(k); OutputMatching.ID; OutputMatching.LIBL; |
| MetaData | HashMap. Содержит информацию о методанных колонок входного потока. | MetaData.ID.Description; MetaData.ID.Derivation; MetaData.ID.SQLType: MetaData.ID.DataElementName; |
| OutputMetaData | HashMap. Содержит информацию о методанных выходного потока | OutputMetaData.ID.Description; OutputMetaData.ID.Derivation; OutputMetaData.ID.SQLType: OutputMetaData.ID.DataElementName; |
| InputColumns | HashMap. Содержит все колонки входного потока | InputColumns.ID; InputColumns.LIBL; |
| OutputRows | List<,HashMap>. Список строк выходного потока. Можно использовать этот объект, когда количество линий выходящих больше чем количество входящий линий. | HashMap curRow=new HashMap(); outputRows[0]=curRow; outputRows[0].ID=0; outputRows[0].LIBL=«First item»; |
Таким образом, есть два способа создания выходного потока:
Какой из способов использовать – зависит от конкретной ситуации.
Чтобы начать использовать Groovy Transformer в ваших проектах, неплохо бы заполнить свойства стейджа Java Transformer:
Ниже я приведу несколько примеров использования Groovy Transformer:
sql = Sql.newInstance( 'jdbc:oracle:thin:@oraclehost:1533/nomdebdd',
'user', 'password', 'oracle.jdbc.OracleDriver' );
sql.eachRow('select id, LIBL_AXE_CENTRAL, SYS_CREATED_DAT as DAT_DEB_VALID from ODS.ODS_AXE_CENTRAL') { it ->
GRow=GTransformer.createOutputRow();
i=0;
it.toRowResult().each{k,v->
i++;
if (v instanceof java.sql.Date)
GRow.setValueAsSQLTyped(OutputMatching.get(k),it.getTimestamp(i));
else
GRow.setValueAsSQLTyped(OutputMatching.get(k),v) ;
}
GTransformer.writeRow(GRow);
}
« Контракт F-U-1009 от 10/01/1960 »
« Договор об оказании услуг F-U-1019 за 10/01/1961 »
« Кон-т номер F-U-1001 от 10/01/1962 »
и нам желательно получить на выходе две колонки на выходе: номер контракта и дату его заключения.
В этом случае можно использовать следующие регулярные выражения:
Тогда для этого примера код Groovy может быть таким (обратите внимание, что используется другой способ генерации выходного потока):
String StrIn=InputColumns.contrat
contRegExp="[A-Z]+(-[A-Z])+(-[0-9]+)"
datRegExp="[0-3]?[0-9](/|\.)[0-1]?[0-9](/|\.)(19|20)?[0-9]{2}"
def matchRegEx(str, regExp) {
matcher=(str=~regExp);
if (matcher.getCount()!=0)
return matcher[0][0];
else
return null;
}
def cont=matchRegEx(StrIn, contRegExp);
def dat =matchRegEx(StrIn, datRegExp);
HashMap curRow=new HashMap();
OutputRows[0]=curRow;
OutputRows[0].contrat=StrIn;
OutputRows[0].numero=cont;
OutputRows[0].date=dat;
Несколько полезных ссылок:
Автор: Geckelberryfinn
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/23308
Ссылки в тексте:
[1] http://geckelberryfinn.ru/fr/GroovyTransformer.html: http://geckelberryfinn.ru/fr/GroovyTransformer.html
[2] http://groovy.codehaus.org/Documentation: http://groovy.codehaus.org/Documentation
[3] http://www.ibm.com/developerworks/data/library/techarticle/dm-1106etljob/index.html: http://www.ibm.com/developerworks/data/library/techarticle/dm-1106etljob/index.html
[4] Источник: http://habrahabr.ru/post/163615/
Нажмите здесь для печати.