Data Lookup via Java Transformations
Introduction
Informatica DQ allows to integrate Java code within Mapplets by using the Java Transformation. Combined with the Service API REST interface, Java transformations can be used to look up additional data from the HPM while executing rules. This is useful if the data cannot be provided by the input ports, e.g., because the data is located in a different entity.
Within the Sample Data Package, the rules Check_MissingAttributes and Structure_Check_* which can be executed on items, products and variants use Java transformations to look up data that is stored in structure groups and structure features.
Step-by-step example
In this example, we will develop a simple Java transformation that retrieves structure group names in a rule that processes items with the entity Article.
Create a new Java Transformation
Use the context menu item Add transformation... and select the transformation Java. The following dialog box appears:
Important is that Create as Active is checked.
The path to all plugins used by the Rest client has to be set in the Properties / Advanced tab:
The plugins are available in the Client_Webservice.zip package.
Include code to retrieve a RestClient instance
Now, we can start to include Java code. First, the Java code to get an instance of the class RestClient has to be provided.
Therefore, the best place is the Java / Functions tab:
The following code has to be included:
private
RestClient getRestClient()
{
initContextClassLoader();
RestClient restClient =
null
;
try
{
Class clazz = Class.forName(
"com.heiler.ppm.webservice.server.internal.InternalRestClient"
);
restClient = ( RestClient ) clazz.newInstance();
}
catch
( ClassNotFoundException e )
{
throw
new
RuntimeException( e );
}
catch
( InstantiationException e )
{
throw
new
RuntimeException( e );
}
catch
( IllegalAccessException e )
{
throw
new
RuntimeException( e );
}
restClient.loginWithBasicAuth(
""
,
""
,
""
, Locale.US );
//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return
restClient;
}
private
void
initContextClassLoader()
{
Thread mainThread =
null
;
Set< Thread > keySet = Thread.getAllStackTraces()
.keySet();
for
( Thread thread : keySet )
{
if
( thread.getName()
.equals(
"main"
) || thread.getName()
//$NON-NLS-1$
.equals(
"WrapperSimpleAppMain"
) )
//$NON-NLS-1$
{
mainThread = thread;
}
}
if
( mainThread !=
null
)
{
Thread.currentThread()
.setContextClassLoader( mainThread.getContextClassLoader() );
}
}
Remarks:
The class com.heiler.ppm.webservice.server.internal.InternalRestClient is a subclass of com.heiler.pim.webservice.client.RestClient that does not use the http protocol and does not use JSON but calls the Java methods directly with the original Java objects. This approach speeds up processing and does not require a login mechanism.
In the future, we will probably provide an option so that IDQ runs in its own process. If this option is active, the standard RestClient has to be used.
The helper method initContextClassLoader sets the class loader for the current thread to the same which is used in the main thread. Otherwise, the classes in com.heiler.ppm.webservice would not be found.
Additionally, the following import statements have to be included in the Imports tab:
import
java.util.ArrayList;
import
java.util.List;
import
java.util.Locale;
import
java.util.Set;
import
com.heiler.pim.webservice.client.EntityItemReference;
import
com.heiler.pim.webservice.client.EntityItemReferenceFactory;
import
com.heiler.pim.webservice.client.RestClient;
import
com.heiler.pim.webservice.client.list.EntityItemTable;
import
com.heiler.pim.webservice.client.list.EntityItemTableRow;
import
com.heiler.pim.webservice.client.list.ListReadRequest;
import
com.heiler.pim.webservice.client.list.ReportQuery;
Press the compile button to ensure that the code does not contain any compilation errors.
Additionally it is recommended to validate a Java Transformation. it could happen that the compilation result does not contain any results, yet the Java Transformation cannot be used due to its invalid state. Reasons for this could be f.e. incompatible java version of the used jar files with the SDK.
Collect input data
For efficiency reasons, the input data has to be collected before a REST called is executed. Otherwise for each row a REST call would be executed leading usually to unacceptable performance.
The easiest way to do this is to define a class Row which includes member variables for all input ports and store them in a list.
A good location for including the class is the tab Java / Helpers. The code may look like this:
private
List< Row > collectedRows =
new
ArrayList< Row >();
private
class
Row
{
String structureGroup;
public
String getStructureGroup()
{
return
this
.structureGroup;
}
public
void
setStructureGroup( String structureGroup )
{
this
.structureGroup = structureGroup;
}
public
String toString()
{
StringBuilder sb =
new
StringBuilder();
sb.append(
this
.structureGroup );
return
sb.toString();
}
}
The code for row creation has to be added in the tab Java / On Input:
Row row =
new
Row();
row.setStructureGroup( inStructureGroup );
this
.collectedRows.add( row );
Executing the Rest call
The REST call should be executed only once after the input for all rows of this batch is available. Note that PIM splits the calls to IDQ, so the data for a maximum of 1000 objects is provided.
The code has to be added in the tab Java / At End:
RestClient restClient = getRestClient();
List< EntityItemReference > structureGroupRefs =
new
ArrayList< EntityItemReference >();
for
( Row row :
this
.collectedRows )
{
structureGroupRefs.add(
new
EntityItemReference( row.getStructureGroup() ) );
}
ReportQuery reportQuery =
new
ReportQuery( structureGroupRefs );
ListReadRequest readRequest = restClient.createListReadRequest();
readRequest.setFields(
"StructureGroupLang.Name(en)"
);
//$NON-NLS-1$
readRequest.setPageSize( -
1
);
EntityItemTable table = readRequest.getRootItems(
"StructureGroup"
, reportQuery );
//$NON-NLS-1$
for
( EntityItemTableRow tableRow : table.getRows() )
{
List< Object > values = tableRow.getValues();
outStructureGroupName = ( String ) values.get(
0
);
generateRow();
}
// it is highly recommended to clear the table for collected rows at end because for performance reasons
// the java transformation class is not always reinstantiated.
this
.collectedRows.clear();