Invoke a Java Method From a Data Policy

From LongJump Support Wiki
Revision as of 19:41, 7 February 2011 by imported>Aeric (→‎Invoke a Java Method From a Data Policy)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Invoke a Java Method From a Data Policy

Invoking a Java method, lets you use the Java APIs to interact with the platform, and do anything else that you can accomplish using Java code.

Method Signature

You can select any method defined in a Java class, as long as it has the required signature:

public void someMethod(com.platform.api.Parameters requestParams);
Considerations
  • The method can be either a static class method or an instance method. An instance of the object is created before making the call.
  • The method must be public, return void, and take a single Parameters argument.

Example: Mask Outgoing Data

This data policy action is built for the Sample Order Processing System. It is designed to be run on the Orders object, which has a field named credit_card_number.

The goal is to mask the customer's credit number, so that only the last 4 digits are displayed on screen. To do that, when the data policy is created in the GUI, the following triggers are chosen:

  • "On View" - When a record is retrieved by the GUI or by an API.
  • "List View" - When a search has generated a list of records that will be displayed in a grid in the GUI, or returned to a program as a response to an API call.

The code checks to see which event triggered the action, and does slightly different things in each case to mask the credit card data.

To set up the data policy:

  1. Create a data policy:
    • Activation Trigger: Action Based
    • Activation Sequence: After Triggering Actions
    • Triggering Criteria: On View and List View
  2. Add Action: Execute Java Method
  3. Select the mask_ccn() method in the OrderPolicies class shown below.
package com.platform.demo.pgms;

import java.util.*;
import java.util.Map.Entry;

import com.platform.api.*;

/**
 * Define data policy actions for Order objects. Remember: Multiple actions can
 * be executed as part of a data policy, so you don't need to do too much in
 * any one action.
 */
public class OrderPolicies
{
    // An object-specfic identifier for a field we're interested in. 
    // In this case: The credit card number field.
    String ccn_field = "credit_card_number";     // Field name, from the GUI
    
    static final String SEARCH_RESULTS = CONSTANTS.SEARCH.SEARCH_RESULTS;
    static final String RECORD_RESULT  = CONSTANTS.RECORD.RESULT;
        
    /** 
     * Mask the credit card number, showing only the last 4 digits.
     * Called when retrieving order information.
     */
    public void mask_ccn(Parameters requestParams) 
    {
        //Functions.debug("In mask_cnn");
        if ("on_search".equals(requestParams.get("policy_action"))) {
            // The policy is being executed as a result of search (list display)
            // A list of search results (displayed records) are in the parameter
            // map. Each record to be displayed is in that list. Modifying the
            // record data changes the values displayed for that record.
 
            //Functions.debug("Masking CCN in a List");
            ArrayList<HashMap> recordList = 
               (ArrayList) requestParams.getObject(SEARCH_RESULTS);
            if (recordList != null && recordList.size() > 0) {
                // each element in the array list is a map that contains the
                // field requested in search result
                for (HashMap record : recordList) {
                    mask_ccn(record);
                }
            }
        }

        if ("on_view".equals(requestParams.get("policy_action"))) {
            // The policy is being executed as a result of view (record view)
            // The parameter map contains record values.
            // Modifying the record changes the values returned.
            
            //Functions.debug("Masking CCN in a Record");
            HashMap record = (HashMap) requestParams.getObject(RECORD_RESULT);
            mask_ccn(record);
        }
    }
    
    /**
     * The workhorse method. Replace the SS# data in the record with 
     * a masked-out version.
     */
    public void mask_ccn(HashMap record) 
    {
       if (record.get(ccn_field) == null) {
           // A given records-list view might not include the credit card
           // number field. In that case, there is nothing to mask.
           return;
       }
       String ccn = (String) record.get(ccn_field);
        if (ccn != null && ccn.length() == 16) {
            //Functions.debug("Masking. ccn="+ccn);
            //Format is xxxxNNNNyyyyMMMM
            String subpart = ccn.substring(12);
            record.put(ccn_field, "xxxxxxxxxxxx" + subpart);
            
            //To mask a social security number:
            //  // Format is xxx-xxx-xxxx.
            //  String subpart = ssn.substring(7);
            //  record.put(ssn_field, "xxx-xx-" + subpart);
        }
        else {
            Functions.debug("Invalid credit crd#: "+ccn);
            // To ensure this never happens, run a data policy on add and 
            // update to check the format and throw an exception to abort
            // the transaction. (In future, it may be possible to invoke a
            // class method for validation, the same as for a data policy.)
            record.put(ccn_field, "Invalid credit card# format: " + ccn);                
        }        
    }
    //...
}

Notepad.png

Note: Selecting List View in the GUI causes a parameters object to be delivered to the method that contains the keyword SEARCH_RESULTS. That keyword applies to records returned in API-based searches, as well as those that were initiated from the GUI to generate a list view.

Learn More:

Example: Validate Incoming Data

This data policy action ensures that the credit number is in the proper format to begin with. (Exactly 16 digits, with no characters or spaces.) The policy action is designed for the Orders object in the Sample Order Processing System, which has field named credit_card_number.

This is an important technique for sophisticated validation. In this case, for example, a credit card number is too big to use a numeric field, while normal validation mechanisms are limited to simple field comparisons. The solution is to use a data policy to parse the data, and throw an exception in the event of an error.

This code throws an exception if the format isn't valid. The exception will abort the transaction before it is committed. If it were activated in after the triggering event ("post-trigger") then the transaction would have to be rolled back. So it is better practice to configure the data policy so it is activated before the triggering event is carried out.

    
    public void validate_order(Parameters requestParams)
    throws Exception
    {
      // Functions.debug("Validating order");
       String errMsg = "Invalid credit card number format. Needs 16 digits.";
       HashMap record = (HashMap) requestParams.getObject(RECORD_RESULT);
       String ccn = (String) record.get(ccn_field);
       if ((null != ccn) && (ccn.length() != 16)) {
           throw new Exception(errMsg);
       }
       for (int i=0; i<=15; i++) {
           char c = ccn.charAt(i);
           if (c < '0' || c > '9') {
               throw new Exception(errMsg);
           }
       }
       // Format validated
       return;
    }

Additional Examples

For many more examples of data policies that execute java code, see Java Code Data Policy Examples.