Custom Forms and View Handlers in Sun Identity Management 8.0

Sun IdM, XPress and myself

Lately I’ve been working in a project for a large corporation implementing an identity management solution based on Sun Identity Management 8.0.

As part of the project, we’re doing a lot of customization to the product. A lot of the customization is done because the built in components are in desperate need of adjustments — especially when it comes to the interface (the built in interfaces have probably never even been looked at by interaction designers or graphic designers), but we’re also adding to the existing functionality.

When customizing IdM, you soon realize that writing logic in XPress (Sun’s proprietary XML based programming language) is a painful process.

Although it is simple enough, XPress code is more tedious to write and maintain, and you also loose the benefits of tools such as good editor support and unit testing. Not to speak of the verboseness of XML, and writing an application in an XML-based language.

Nested <cond> tags, complex <rule>s (which could take 10s of lines in XPress compared to a few in Java), and too much logic in your <form>s or <field>s soon turn out to be a real mess. It’s just plain horrible.

I’m sure XPress was, or at least ought to, never have been meant for implementation of complex business logic, and that it might be better suited for simple customizations made by non-programmers. Still, from what I see on the WWW, "complex" logic (at least beyond doing string replacement, simple conditionals, etc.) is often placed in XPress forms and rules.

Unfortunately, there is only little documentation on IdM’s Java API available, though, and the JavaDocs are terrible. Further, the documentation that do exist, is, as is the case with many commercial products, of poor quality. Therefore, you might soon find yourself in XPress hell.

This is why I want to share some of my experiences doing customization to IdM in the Java layer — such as creating custom views, and integrating your Java code with XPress.

Basic Java Calls in XPress

The IdM docs reveals that we can do simple Java calls from XPress by using <invoke> and <new>.

Example:

<defvar name="myDateStr">
    <invoke name="dateToString" class="com.waveset.util.Util">
        <new class="java.util.Date" />
        <s>yyyy-MM-dd</s>
    </invoke>
</defvar>

Compared to Java:

import com.waveset.util.Util;
import java.util.Date;

String myDateStr = Util.dateToString( new Date(), "yyyy-MM-dd" );

Which one looks better?

Of course, this is just the most basic example, and XPress might suffice for this kind of stuff. But if you want to do even slightly more complex stuff, or for example database querying, XPress turns out to be no fun at all.

If you have worked with XPress for more than a couple of weeks you already know this. And that’s why you’ll want to do more of the logic in Java.

Custom View Handlers

As you probebly know, views are an important concept of IdM. When the built in JSPs are loaded, different views are generated in the back end. The views hold variables that are available to XPress forms.

As part of the aforementioned project, we have built reporting functionality that is not part of the product (could not be accomplished with the built in Auditor). As part of this, we insert and fetch records from separate database tables.

Doing all of this in XPress is, if you ask me, a no-no. It is just way too messy and a sure source of bugs.

We still want to use the built in functionality for displaying the data, though, and take advantage of built in validation, etc.

Where to start

What we wanted to do was to use

  • XPress for viewing the data
  • XPress for <validation>, setting required fields, etc.
  • Custom Java code for logic

Since what we’re doing is to extend the product, we also need a custom JSP. Using a custom JSP would have been the most clean way to go if we were to do it all in XPress anyway — instead of using one of the built in JSPs intended to serve a different purpose (loading a completely random view).

To accomplish our goal, what we have to build is therefore as follows:

  • A custom XPress form for displaying the data
  • A custom JSP
  • A custom Form (Java), used by our JSP
  • A custom View Handler, for getting, setting, and refreshing the view.

Custom XPress form

Below is an examle of an XPress form that prints out data which has been fetched from an external source and populated into the view by our custom view handler (please pay no attention to the lowercase and invalid XPress tag names — it is just WordPress’ attempt at cleaning up my code):

< ?xml version='1.0' encoding='UTF-8'?>
< !DOCTYPE Configuration PUBLIC 'waveset.dtd' 'waveset.dtd'>
<configuration id='#ID#UserForm:MyCorp-MyCustomForm' name='MyCorp-MyCustomForm'
	wstype='UserForm'>
	<extension>
		<form noDefaultButtons='true'>
			<field>
				<display class="SortingTable">
					<property name="columns">
						<list>
							<s>Role</s>
							<s>System</s>
							<s>Application</s>
						</list>
					</property>
				</display>
				<fieldloop for="role" in="roles[*].name">
					<field name="roles[$(role)].name">
						<display class="Label" />
					</field>
					<field name="roles[$(role)].system">
						<display class="Label" />
					</field>
					<field name="roles[$(role)].application">
						<display class="Label" />
					</field>
				</fieldloop>
			</field>
		</form>
	</extension>
	<memberobjectgroups>
		<objectref type='ObjectGroup' id='#ID#Top' name='Top' />
	</memberobjectgroups>
</configuration>

Custom JSP

The custom JSP used to present our custom XPress form should look like any other IdM JSP. All we want to do, is to use our custom form.The contents of the JSP could therefore look like so:

< %@ include file="../includes/copyright.jsp"%>
< %@ include file="../includes/headStart.jsp"%>
< %@ include file="../includes/headEnd.jsp"%>

<jsp :useBean id="form" scope="page"
	class="com.mycorp.myproj.ui.web.MyCustomForm" />

< %
	try {
		form.setPostURL("custom/myCustom.jsp");
		form.setNextURL("custom/myCustom.jsp");
		form.setCancelURL("custom/myCustom.jsp?newView=true");
		String url = form.process(req);
		if (url != null) {
			LoginHelper.redirect(req, out, url);
			return;
		}
	} catch (Throwable th) {
		form.addError(th.getLocalizedMessage());
%>
< %=com.waveset.util.Util.stackToHtmlComment(th)%>
< %
	}
%>

< %@ include file="../includes/bodyStart.jsp"%>

< %=form.generateHTML()%>

< %@ include file="../includes/bodyEnd.jsp"%>

As you can see, the JSP doesn’t contain much special. The most important part is using a custom Form, or JSP bean (line 5).

Custom Java Form

The custom Java form that we need does not contain much code. It just provides us with the basic functionality for using the custom view:

package com.mycorp.myproj.ui.web;

import com.waveset.ui.util.ViewSource;
import com.waveset.ui.util.GenericViewSource;
import com.waveset.ui.util.GenericEditForm;
import com.waveset.util.WavesetException;

public class MyCustomForm extends GenericEditForm {

	@Override
	public ViewSource createViewSource() throws WavesetException {
		GenericViewSource vs = new GenericViewSource(req);
		vs.setViewId("MyCustom:customView");
		return vs;
	}

}

When this form is loaded, IdM will try to load a view handler named “MyCustomViewer” (based on the part of the view id before the colon + “Viewer”) of package com.waveset.view and assign the ID “customView” to the loaded view. We therefore create a new viewer for this form.

Custom View Handler

The custom view handler is our real hook into Java layer. From here we can manipulate the view and call whatever code we might want to, such as perhaps a Topic Maps engine :)

package com.waveset.view;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.waveset.object.Form;
import com.waveset.object.GenericObject;
import com.waveset.object.ViewMaster;
import com.waveset.object.WavesetResult;
import com.waveset.util.WavesetException;

/**
 * @author Trond K. Pettersen
 */
public class MyCustomViewer extends AbstractViewHandler {

	// The getView() method should only return a GenericObject
	// which in fact is the view. This is called on the initial
	// loading of our view.
	@Override
	public GenericObject getView(ViewMaster vm, String id, Map options)
			throws WavesetException {
		GenericObject view = new GenericObject();
		view.put("roles", findAllRoles());
		return view;
	}

	// Refreshes the view. This is called if the form is e.g.
	// submitted by the user, or the view for some other reason
	// should be refreshed.
	@Override
	public GenericObject refreshView(ViewMaster vm, GenericObject view,
			Map options) throws WavesetException {

		// We could e.g. refresh the data
		view.put("roles", findAllRoles());

		// We could also do stuff like
		String command = view.getString("display.command");
		if("deleteRole".equals(command)) {
			// deleteRoleByIdOrSomething();
		}

		return view;
	}

	// This is called initially and returns the form associated
	// with this view. The formId is the ID of our custom XPress form
	@Override
	public Form getForm(ViewMaster vm, GenericObject view, String formId,
			Map options) throws WavesetException {
		formId = "MyCorp-MyCustomForm";
		return super.getForm(vm, view, formId, options);
	}

	// We just want to make sure that nothing special happens if
	// the checkinView is called on our view
	@Override
	public WavesetResult checkinView(ViewMaster vm, GenericObject view,
			Map jspOptions) throws WavesetException {
		return new WavesetResult();
	}

	// We just want to make sure that nothing special happens if
	// the checkoutView is called on our view
	@Override
	public GenericObject checkoutView(ViewMaster vm, String id, Map options)
			throws WavesetException {
		return getView(vm, id, options);
	}

	private List<GenericObject> findAllRoles() {
		// Just initialize some test data
		List<GenericObject> roleList = new ArrayList<GenericObject>();
		GenericObject aGenObj = new GenericObject();
		aGenObj.setId("role92001");
		aGenObj.setName("someRole");
		aGenObj.put("application", "someApplication");
		aGenObj.put("system", "someSystem");
		roleList.add(aGenObj);
		return roleList;
	}

}

And voila. We have created a custom form and view handler and can do whatever we want to do in Java :)

Screenshot

Now tell me you don’t also want to change that interface…

(Since WordPress is such a crappy tool to post <code> from, have a look at the example code).

Previous Post
Leave a comment

15 Comments

  1. Holly

     /  27/02/2009

    This article is very helpful. I am just started to get my feed wet for a SunID project with no help at all. I run into this article, thank you so much for writing this down.
    it is so helpful.

    Reply
  2. Hi Holly. Glad I could help. I remember how it felt when I first started working with XPress: “what the heck?!” (I still feel this way:D)

    Reply
  3. B

     /  20/03/2009

    I agree that this is very useful, especially since I too am just diving in and getting my hands on anything out there to read!

    I think they created XPRESS because the number of people with XML skills is larger than Java… though how frustrating it would be to try and write this in XML without understanding the Java exceptions when errors occur.

    I hope you mention this site in the Sun Idm forum so others can find this. Though when I did a google search, this is the second link – good job!

    Reply
  4. Gaurav

     /  11/04/2009

    Hi,

    I would like to try it out.
    Can you please tell me what all configurations will be required to run this code? It will be really helpful to me.

    Thanks
    Gaurav

    Reply
  5. Gaurav: there is no special configuration required. It’s a standard java web application.

    You only have to create the java packages and classes as specified in the example code. Then compile the source code and deploy it as part of your IdM application (under webapps/idm/WEB-INF/classes in Apache Tomcat, for example), upload the example XPress form and test the result by viewing the custom JSP.

    Reply
  6. Etienne

     /  23/04/2009

    Hi,

    I used your article to build a custom page with IdM 7.1. My form uses the User View with a custom Form and a custom JSP. I built my custom view handler by extending the UserViewer class, but that causes a weird problem.

    When using the UserViewer class as a View handler, IDM resolves the Form by itself using the System Configuration (Form configured on the page Configure -> Form and process mapping in IDM Admin). Logged in with a user, it returns the Form with the Form type: endUserChangePassword.

    I want to have a custom page returned, so in the method getForm(), like in your example, I do:

    formId = “MyCustomForm”;
    return super.getForm(vm, view, formId, options);

    The result seems to work since my custom form is displayed. However, the “Checkout view” and “Refresh View” steps don’t go through my Form, only the “render HTML”. This method override fails to affect the global behaviour of my form… And so all my Derivation or Expansions set in the form are not executed, which is a huge problem.

    Any ideas?! I can provide the necessary code if anybody can help.

    Thanks.

    Reply
  7. Vladimit Tsichevski

     /  20/05/2009

    Hi,

    using custom viewers is the way the IdM creators did most of IdM itself. Unfortunately, the technology is completely undocumented :-( During the last year of implementing SUN IdM-based system, I digged out the way view handlers might be used and I’m completely happy with them. Did you ever thougth of creating a site dedicated specially to “IdM Java programming”, so we and other people can share their experience in the field?

    Reply
  8. You’re spot on on the undocumented part. Sometimes it’s really frustrating … isn’t it interesting how open source communities often have way better documented systems than those costing thousands of dollars?

    I def. see the need for a IdM java programming site, and it would probably be a good idea to set up such a thing. But that’s not for me ;)

    Reply
  9. Alex

     /  29/06/2009

    Hi…. how can I print the content of an IDM form??

    Reply
  10. Stavae

     /  10/09/2009

    Hi :=)

    I have been fighting with “Customizing Forms” for nearly 2 weeks now!

    It’s amazing just how SUN’s website offers no help whatsoever! They always talk about WHAT needs to be done. But, they never explain HOW it should be done.

    Most of the things I was able to accomplish — I did via trial & error.

    I was finally able to modify a User Form, and import it into the IDM instance.

    It’s a simple User Form for inputing such details as : firstname, lastname, address, telepghone, etc, etc.

    It seems to be working fine.

    However, there is one big problem : after inputing the data, I save it without a hassle. But, whenever I try to VIEW or EDIT that user, the form shows up blank.

    In other words, I get an empty Form. Most of the data I input has disappeared.

    The only fields that still retain their data are : firstname, lastname, email.

    I am guessing that the reason why this is so is because : for all the field names, I used the prefix “global” (global.firstname, global.address, etc, etc)

    But, probably, IDM does not recognize a fieldname such as : “global.hometelephone”, or “global.height” or “global.weight”

    I don’t know if this is important.

    If it is, then, somewhere in IDM’s repository, there is a list of all valid field names, all beginning with “global”. And ONLY those field names are acceptable.

    Is this the case??

    Does anyone have an idea of what my problem is?

    Thanks

    Reply
  11. Trond Pettersen

     /  15/09/2009

    @Stavae:
    You might want to look into IDMSchemaConfiguration where you can set up custom IDMAttributeConfigurations.
    See e.g. http://docs.sun.com/app/docs/doc/820-5597/ahvbi?a=view

    Reply
  12. sunny ajmera

     /  05/03/2010

    Hi,

    Thanks for this helpful article. I would really appreciate if you could help me on achieving following requirement:

    I have few custom extended attributes in SIM like groupname, endpointname and I have a custom java resource adapter configured on SIM 7.1. What I am doing is that I am trying to fetch value of these extended attributes in my custom java resource adapter and provision resource with these values.

    For the first time, while creating the user and assigning the custom java resource adapter in SIM, I am able to retrieve value of my extended attributes using following function:
    public WSUser getUser(WSUser user) {
    WSuser user;
    user.getAttributeValue(“groupname”);
    }
    and provision the user in the resource.

    However, when I again click on the user, I am getting null value. It seems to me that only while submitting the form, I am able to retrieve the value. I have tried various others methods also but none of them worked for me.

    I would really appreciate if you could provide me some pointers.

    Thanks
    Sunny
    ajmera.sunny@gmail.com

    Reply
  13. Anders

     /  30/05/2010

    Hi there Trond,

    In the createViewSource method in class MyCustomForm there is a, seemingly unknown, identifier ‘req’. The code does not compile because of this. How can I fix the code?

    Best Regards,
    Anders

    Reply
  14. Hi Anders,

    You are absolutely right. I would suspect it is supposed to be the HttpServletRequest or the IdM specific req variable coming from the JSP. The GenericEditForm doesn’t decalare this field? I guess obviously not since your code doesn’t compile, but this was written for Sun IdM 8.0… Sorry I included such a bad variable name that even I as the author can’t tell you what it is. As for how you can fix this problem, you’d have to look into what type of argument the GenericViewSource constructor expects.

    Unfortunately, I don’t have my old IdM code lying around anymore, though, and even left the IdM world about a year ago — with no plans of going back.

    Reply
  15. Michael Andrejews

     /  18/04/2011

    I think you solved the problem right now ;) but it could be interesting for others:

    change the ‘req’ with ‘this.getRequestState()’ and it is possible to compile the code.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

  • @twitter

  • Tags

  • Topics

  • Recent Comments

  • Topic Map Feeds