Data Lookup via Java Transformations

How to look up additional data from PIM while executing rules

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:

images/download/attachments/35914348/JavaTransformation1.jpg

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:

images/download/attachments/35914348/JavaTransformation2.jpg

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:

images/download/attachments/35914348/JavaTransformation3.jpg

The following code has to be included:

Method getRestClient
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:

Imports
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:

The class Row
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:

Code snippet for tab 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:

Code for REST call
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();