IM Developer Guide – Custom Control V3
About this guide
This document gives an overview of the installation of PhenixID Identity Manager. Additional information is found on PhenixID web site or through PhenixID support.
Summary
This document explains Custom Control V3 in PhenixID Identity Manager. It is assumed reader is familiar with both the Identity Manager and Identity Manager Configurator. The document is a part of a collection of documents explaining how to extend and customize Identity Manager. In addition there is also Javadoc and sample code.
Mentioning of abstract base class assumes these are used when developing custom code.
First time readers are recommended to read overview document, IM Developer Overview.
Definition
Write your own custom controls to extend the standard functionalities of the PhenixID Identity Manager (IM).
Implementation of an IM solution is about designing forms with controls representing attribute values of objects from a data source, usually an LDAP repository. The forms are built using the IM Configurator Tab designer which provides a rich set of controls to represent data, ranging from simple text fields to complex multi-list searches.
Although the controls provided with IM provides sufficient functionalities for most situations, sometimes a new control (custom) must be created.
Custom controls in general have no restrictions as standard controls have. Required restrictions and validations must be provided by the developer writing the code.
Requirements
IM is written in Java and any extension, including custom control extensions, must be written in Java.
The current IM version requires Java 1.8.
Tomcat has been used for testing the CCv3 mechanism but any web container supported by IM may be used.
The presentation layer is web and anything that can be rendered in an HTML DIV tag by a WEB container can be used for presentation.
Custom controls where the presentation layer is coded with HTML or in a Java Server Page (JSP) are most common, but anything that Tomcat can render may be used.
Configuration and installation of a custom control
A custom control consists of a java component and a presentation component.
In its simplest form, a custom control consists of a Java class file and a JSP file.
The custom control components must be available to IM Configurator for configuration purpose and to the web container for execution.
This guide assumes that the custom control java code is packaged in a jar file, which is recommended, but class files can be used as well.
IM Configurator
To configure a custom control with the Tab designer, IM Configurator must have access to the jar file holding the java components from the custom control.
This is done by copying the jar file into the folder /ext/lib in IM Configurator before starting the Configurator.
Once configured, custom controls can be added to IM forms with the IM Configurator Tab designer.
IM Web Edition
To run a CCv3, the web container (IM Web Edition) must have access to both the java component and the HTML/JSP presentation code.
This is done by copying the jar file containing the java component into the customer/extension/lib directory.
The presentation component must be placed in such a way that IM can find it, for example: customer/extension/web/jsp/myCustomControl.jsp.
The path (URI) to the presentation component must be provided by the java component in the getURL method.
Note: To re-start Tomcat is required.
To Develop a custom control
A custom control consists of two components:
- An instance of a java interface that provides custom logic.
- A presentation component providing the visual part, often a jsp file.
The presentation component
The presentation component is rendered by the web container within a <DIV> tag in an IM managed form.
IM will tell the web container to render the content of the presentation component within a DIV-tag that was positioned on a form using the Tab designer. Thus the presentation can be anything that the web container can render. Usually, a JSP or an HTML page but other web technologies may work as well.
For the purpose of demonstrating this mechanism a JSP page will be used.
The JSP/HTML code execute within an IM JSP form and has access to the resources of that form. IM forms use Dojo components, which provides a rich set of functionality, and using Dojo to write the custom control will give a uniformed style but it is not necessary. Custom control components can be developed and styled independently of IM forms.
For example, the content of a JSP/HTML file may be:
- a button
- a clickable image
- a grid (Dojo) or a table (HTML)
The component may be used to present data, allow to update the data or capture user action.
A simple example to render a button:
<label for="get">Get</label> <input type=button id="get"></input>
To make this example a little more elaborate you can add styles in the same file:
<style> #get { width:50px; height: 30px; } </style> <label for="get">Get</label> <input type=button id="get"></input>
This is to show that any HTML text can be used and styled as necessary.
Make sure that the styling is specific so that it doesn’t affect the whole form since the component runs in the scope of the current tag.
It is also possible to place the styling in external style-sheets and link to them or import them.
Another example, showing the ability to use dojo components:
<button dojoType='dijit.form.Button' id="myButton" onClick="alert( 'Done!');">MyCustomAction</button>
In addition to present the Dojo framework, it is also showing the ability to add javascript code to do processing on the client.
The java component
The Java component is the interface that IM will use to manage the custom control in different stages of the execution.
The base class providing this interface is se.nordicedge.controls.CustomComponentBase. It is strongly recommended to use this base class, and it provides the following members access to subclasses.
public HttpServletRequest request; public HttpServletResponse response; public NEIDMgmtSession userSession; public AppAttributeObject attribute;
The request and response is the current http context for the custom control.
The userSession object is the current IM context.
The attribute is the IM attribute object.
How these members are used in the custom control code will be covered in this description.
The base class provides a number of public member methods that can be overridden by the component. Only a few are mandatory, depending on the needs.
Methods
public String getDescription();
Design time method.
Override this to return a description of the custom control to be presented in the Tab designer.
public Integer getRequieredNrOfParameters()
public String getPrompt(Integer label)
These two design time methods are used to present the parameters that must be set in the designer. This is the way for the designer to pass information to the custom control. It could be used to set the height and width of a text box.
The parameters are numbered from 0 and has no upper limit.
The first parameter is reserved to IM so a typical implementation of the two could be:
public String getPrompt(Integer label) { switch (label) { case 0: return ""; case 1: return "Height (pixels):"; case 2: return "Width (pixels):"; default: throw new RuntimeException("Bad number of prompts "+ label); } } public Integer getRequieredNrOfParameters() { return 3; }
public void initialize()
This method is called when the custom control is about to be displayed. Which is when the user selects the form for the first time for the current object.
This is the place to do one-time initialization of the control.
public String getURL()
This method is called every time before the form is displayed and must return the URL of the file to be rendered as the custom control.
It is called after the initialize for the current object.
This is the place to update any cached data that is about to be presented in the control.
public Boolean storeState()
This method is called when IM requires the state of the form to be stored.
This happens when the content of the attribute is modified, when another form is selected and when the content of a form is saved with the save or save-exit buttons.
This method is called regardless of the attribute configuration.
The values can be stored in the catalog or cached using the setCCDataStore.
The cached values can be used when the presentation component is rendered.
public Boolean preSave()
This method is called when the content of an attribute has been changed and about to be saved in the LDAP data store.
With this method the custom control can modify the value to be stored, e g formatting, or can tell IM to skip storage altogether.
The returned value from this method is considered a veto. There are two constants defined in the base class for this purpose.
DO_NOT_SAVE // IM wont save the attribute DO_SAVE // IM will save the attribute
The attribute must be configured to be editable and not display-only for the method to be called.
public void postSave()
This method is called after IM has successfully stored the attribute in the LDAP data store.
public void postRequest()
This method provides callback functionality. The request could be an Ajax call from the custom control to fetch some data to be presented in the custom control, for example.
The format of the URL is
<context path>/NEIDMgmt?action=postCustomControl&name=<attribute alias name>
The attribute alias name is the name IM uses in the presentation layer, i.e. in the JSP code for the web form. The alias can either be retrieved from the userSession
userSession.getAliasName()
or from the request parameter atname
request.getParameter("atname")
The common use of this method is to return some data via the http response. An example is the following.
public void postRequest() { response.setCharacterEncoding("UTF-8"); try { response.getWriter().println(“Some data to be returned”); } catch (IOException e) { e.printStackTrace(); } }
public boolean getOption(int paramNumber, boolean defaultValue)
public String getOption(int paramNumber, String defaultValue)
These two methods are used to get the value of custom control parameters that are set at design time. A typical use for this is to grab styling parameters in the JSP scriptlet of the custom control.
public void setCCDataStore(Object ccDataStore)
This method provides cache functionality.
Typically used to store some kind of state one may want to use later with the custom control.
public Object getCCDataStore()
This is the counterpart to setCCDataStore.
The method returns any object associated with the attribute.
Sample application
To clarify the use of IM Custom control version 3 we will present a simple but fairly complete custom control.
The control is a simple password entry form. The form has two text fields and a button to generate a new password.
There is basically no logic behind the control and the password generation algorithm is trivial to say the least.
The purpose is to show how a custom control is designed, not how to generate passwords.
The control consist of a presentation component ccv3.jsp and a java component ccv3.java and we will go through the design of both components.
The actual design of the presentation is html code in the jsp.
<div class="row"> <label class="left_label" id="pwd1Label" for="pwd1">Password</label> <!– Use the name attribute, else it wont be passed in the request –> <input class="input_field" type="text" id="pwd1" name="pwd1" value="<%=pwd%>"></input> </div> <div class="row"> <label class="left_label" id="pwd2Label" for="pwd2">Repeat</label> <input class="input_field" type="text" id="pwd2" name="pwd2" value="<%=pwd%>"></input> </div> <div class="row"> <button type="button" id="genPwd" onclick="generate();">Generate</button> </div>
The code is standard HTML code, commonly styled to fit a <div> in the IM form where the custom control is placed. Note that the name attribute must be set. Otherwise the attribute value isn’t passed to the server in the request.
The Generate button will execute the generate() method which is defined as
<script language="JavaScript"> /* * Tell the server to generate a password */ function generate() { dojo.xhrPost( { url: "<%=request.getContextPath()%>/NEIDMgmt?action=postCustomControl&name=<%=aliasName%>", handleAs: "text", timeout: 30000, load: function(response, ioArgs) { dojo.byId('pwd1').value=response; dojo.byId('pwd2').value=response; // Make sure storeState is called changedAttribute('<%=aliasName%>'); return response; }, error: function(response, ioArgs) { console.error("HTTP status code:",ioArgs.xhr.status); return response; } }); } </script>
This javascript method will post a request to the url in which the control alias name is used, this will instruct IM to call the postRequest method of the custom control associated with that attribute.
When the call is returned (asynchronously), the text fields are updated and IM is instructed that the attribute has been updated.
The rest is Dojo functionality.
The alias name of the attribute is retrieved from the request in a scriptlet code executed when the jsp is compiled in the server.
/* * The alias name, this is what the server know us by */ String aliasName = ""; if (request.getParameter("atname") != null) { aliasName = request.getParameter("atname"); }
There is also scriptlet code to find any values that are cached from a previous invocation/rendering of the custom control for the current object, which happens when users navigate forms or choose to save some information by pressing the Save button.
/* * The session and a helper to access the server side custom control object */ NEIDMgmtSession userSession = (NEIDMgmtSession) session.getAttribute("userSession"); AppAttribHelper helper = userSession.getAttributeHelper(aliasName); HashMap<String, String> cache = null; ccv3 cc = (ccv3) helper.getAttrib().getObject(); String pwd =""; /* * Display the cached password if there is any */ if (cc.getCCDataStore() != null) { cache = (HashMap<String, String>) cc.getCCDataStore(); if (cache != null) { if (cache.get("pwd1") != null) { pwd = cache.get("pwd1"); } }
The serverside java component is initialized by the following code
public void initialize() { Object aCache = getCCDataStore(); if (aCache != null && HashMap.class.isAssignableFrom(aCache.getClass())) { cache = (HashMap<String, String>) aCache; } else { cache = new HashMap<String, String>(); } setCCDataStore(cache); }
This code tries to find a cache that has been stored on the attribute. The first time the method is called for the current object this will fail and a new cache object is created.
IM then calls the getUrl method to retrieve the URL to the code that is to be rendered (the presentation component)
public String getURL() { return "customer/test/jsp/ccv3.jsp"; }
When IM leaves the custom control (user selects another form) the storeState method is called, giving the custom control the chance to store the state of the control. All the named input elements of the HTML code are present in the request and may be retrieved from there as in the following
public Boolean storeState() { addToCache("pwd1"); setCCDataStore(cache); return Boolean.FALSE; } private void addToCache(String prm){ cache.put(prm, (String) request.getParameter(prm)); }
Here we put the current value of the request parameter “pwd1” in the cache (a HashMap) and sets this on the attribute. “pwd1” represent the value of the first text input field. We could as wall store both but since they should match the first should be sufficient. Again, this is a trivial implementation.
The last component of the custom control is the generation method called by IM as a result of the xhrPost call in the presentation component.
IM uses the attribute alias name to find this custom control and calls the postRequest method as
public void postRequest() { response.setCharacterEncoding("UTF-8"); try { response.getWriter().println("Password"+counter++); } catch (IOException e) { e.printStackTrace(); } }
This method generates a password and returns the generated password in the response to be rendered in the client.
This example demonstrates the powerful mechanism from IM custom control v3 that can be used for everything in the framework of an IM form.
The complete sample code is attached and a more elaborated password control is delivered with the IM product.