Skip to Content

This time I’m going to show you a new feature that came with SP4. Input validation in Java.
This is a great feature, it allows you to validate user input in the UI
and send error messages back to the UI before any value gets updated in the database. The
validation checks need to be written in Java and they need to be deployed on the WebAS that
runs the IdM UI. This feature is called Extension Framework.

Here is the document on SDN that explains what you have to do.

The document is pretty straight forward and I’m not going to rephrase everything again. But a concrete
example of attribute input which is validated would be nice. So I’ve built a simple UI task which allows to set a “valid to” date on a role.

image

image

 

Before doing so I needed to assign the attribute MX_VALID_TO to the entrytype MX_ROLE, otherwise this attribute would not be selectable as shown in the second screenshot.  What I’m trying to do now is to validate the date.

Let’s say we only want to accept dates which are no more than 365 days in the future. If the valid to is later than this delay the input won’t be accepted and an error message will be shown to the user.

First, let’s have a look at the Java code which does the validation. The date attribute has the format YYYY-MM-DD. Here’s the coding of the function onSubmit:

 

// not very clean, but ok...
  String date = submitData
           .getChangeList()[2].getAttributeValue();

  // cut the date into pieces
  StringTokenizer st = new StringTokenizer(date, "-");
  String year = st.nextToken();
  String month = st.nextToken();
  String days = st.nextToken();

  // store the date in a calendar object
  Calendar calendar = new GregorianCalendar();
  calendar.set(
       Integer.parseInt(year),
     Integer.parseInt(month) - 1,
        Integer.parseInt(days));
                    
  // get "now" as calendar
  Calendar now = new GregorianCalendar ();
               
  // convert into a long value storing the milli seconds
  long dateInMillis = calendar.getTimeInMillis ();
  long nowInMillis  = now.getTimeInMillis ();
               
  // valid to must be greater than now,
  // we don't accept dates in the past
  if (dateInMillis<nowInMillis) {
    throw new IdMExtensionException ("MX_VALID_TO must be a " +
                       "date maximum 365 year in the future.");
  }
               
  // compute 365 days in milliseconds
  long _365days = 365*24*60*60*1000;
               
  if (dateInMillis > nowInMillis + _365days) {
    throw new IdMExtensionException ("MX_VALID_TO must be " +
                   "a date maximum 365 year in the future.");
  }

  return null;

So this is the code that does the trick. After deployment of the ear you need to set the class name on the result handling tab of the task:

image

 

There was only one problem with it when I tried to make it run.

The document says that the jar file sap.com~tc~idm~jmx~extfwk~default.jar needs to be added to the build path. In addition the ear needs a reference to a package which also contains this jar.On my installation, the example didn’t run until I removed the jar from the war file that is inside the ear file.

But let’s come back to this validation thingy.  Here is now what it looks like when I enter an invalid date:

 

image

 

… to make the example complete.
I would like to add some more thoughts about how to use this feature in practice.

Imagine you have a couple of UI tasks that need input validation. You could now write one validation class for each of the UI tasks, package them into the ear and deploy them. The problem I see is that the person who does the Java coding is probably someone else from the person doing the UI stuff, so here you create a dependancy which makes the whole thing a little error prone. It would be desirable to have only one validation class that takes care about all validations. I’ve tried to address this. Please look how my validation class looks like:

  private Map validatorByTaskGuid = new HashMap();
  private static final String TASKGUID_SIMPLE_ROLE_TASK =
                // 295/Simple Role Task
          "124D6912-01C4-40B5-9853-1C964A28ADC7";
     

  public IdMInputValidator()
  {
    validatorByTaskGuid.put(
       TASKGUID_SIMPLE_ROLE_TASK,
       simpleRoleTaskValidator);
  }

  public IdMValueChange[] onSubmit(
          Locale locale,
          int subjectMSKEY,
          int objectMSKEY,
          Task task,
          IdMSubmitData submitData)
          throws IdMExtensionException
  {
    String guid = task.getGUID();

    ITaskProcessing itp = (ITaskProcessing)
        validatorByTaskGuid.get(guid);

    if (itp == null) {
      return null;
    }
    else {
      return itp.onSubmit(
          locale,
          subjectMSKEY,
          objectMSKEY,
          task,
          submitData);
    }
  }

  private ITaskProcessing simpleRoleTaskValidator = new ITaskProcessing() {
    public IdMValueChange[] onSubmit(...) {
        // this is where the above coding goes...
    }
  };

So the logic is that we have one class that does the validation for all UI tasks. There is a hash map inside the validing class that stores references to the “real” validating classes (which are anonymous classes in my case). On startup the validating classes are stored with the task guid of the task they are going to handle. The advantage of the guid is that it is stable across system boundaries so this code will also work when your code has been transported from DEV to QA, for instance.

In order to get the guid, execute the following SQL statement:

select taskguid from mxp_tasks where taskid=295
To report this post you need to login first.

1 Comment

You must be Logged on to comment or reply to a post.

  1. Timothy Baggett

    Kai,

    I have followed the PDF yet I have issues running the code. It seems the UI cannot find my java code. The error spit out by the UI is the following:

    java.lang.NullPointerException: while trying to invoke the method com.sap.idm.wd.wf.entrydetails.wdp.IPublicEditEntry$IEntryElement.modelObject() of an object returned from com.sap.idm.wd.wf.entrydetails.wdp.IPublicEditEntry$IContextNode.currentEntryElement()

    Any ideas?

    (0) 

Leave a Reply