Skip to Content

In this second and last part of my blog series regarding SAP NetWeaver Identity Management (IdM) and UWL I want to focus on the UWL connector side.

In the first part (SAP NetWeaver Identity Management: Approval Items in UWL (Part 1)) I described what IdM functionality is used in order to retrieve the required information. Now we will look at how to develop the UWL connector for displaying the items in the UWL.

The implementation uses the portal system landscape and the AS Java JDBC Connector service. This gives you maximum flexibility without the need to fiddle around with property files and jdbc drivers. My implementation only focuses on MS SQL server but it should be easily extended to also support an Oracle database. The system landscape object used here already provides you with the property “dbtype” which you can evaluate in your coding.

h3. Set up a new Development Project

The first step is to create a new development project in your NetWeaver developer studio.
You do this as follows:

    • Universal Worklist (UWL) -> UWL Custom Connector Project
    • Enter the name of your project
    • give your connector a name (e.g. IdM_UWL_Test), provide a company prefix for the implementation (e.g. “com.test”), a description and very important a task name (e.g. “uwl.test.idmtask”)

h3. The Coding

In your class “IdM_UWL_Test” you need to implement the method getItems()

+  public ConnectorResult getItems(
    UWLContext context,
    String itemType,
    ConnectorFilter connectorFilter,
    String system)

    throws ConnectorException {</p><p>    ConnectorResult result = null;
    List items = null;</p><p>    TaskList tl = new TaskList(context.getUser().getName());
   
    Properties connection = new Properties();
   
    // get Info from System Landscape
    ISystems systemSrv = (ISystems)PortalRuntime.getRuntimeResources().getService(ISystems.KEY);
    String systemId = systemSrv.getSystemId(system);</p><p>    try {
      // create initial context for PCD system lookup
      Hashtable env = new Hashtable();
      env.put(Context.INITIAL_CONTEXT_FACTORY, IPcdContext.PCD_INITIAL_CONTEXT_FACTORY);
      env.put(Context.SECURITY_PRINCIPAL, context.getUser());
      env.put(IPcdContext.PCD_PERSONALIZATION_PRINCIPAL, context.getUser());
      env.put(Constants.REQUESTED_ASPECT, PcmConstants.ASPECT_SEMANTICS);
      InitialContext iCtx = new InitialContext(env);</p><p>      // lookup the IdM portal system object from PCD
      ISystem sys = (ISystem) iCtx.lookup(systemId);
    
      //lookup workflow url in IdM portal system object
      StringBuffer workflowUrl = new StringBuffer();
      workflowUrl.append(sys.getAttribute(“protocol”).toLowerCase());
      workflowUrl.append(“://”);
      workflowUrl.append(sys.getAttribute(“hostname”));
      workflowUrl.append(“:”);
      workflowUrl.append(sys.getAttribute(“port”));
      workflowUrl.append(“/”);
<em><br />      //changed on Nov 14th, 2008 in order to also work with version 7.1<br />      String appContext = sys.getAttribute(“appcontext”);<br />      idm_version71 = (!appContext.equals(“workflow”));<br />      if (idm_version71) {<br />        </em>workflowUrl.append(“webdynpro/dispatcher/sap.com/tcidmwd~workflow/ProcessRequest”);
      } else {
        workflowUrl.append(sys.getAttribute(“appcontext”));
        workflowUrl.append(“/approvalStep2.php”); 
      }
      idm_workflowUrl = workflowUrl.toString();<em><br />    <br />      //lookup id store number in IdM portal system object<br />      connection.setProperty(TaskList.CFG_IDM_IDS_ID,sys.getAttribute(“idstore”));<br />    <br />      //lookup the jdbc datasource name and database type in IdM portal system object<br />      connection.setProperty(TaskList.CFG_DB_DATASOURCE, sys.getAttribute(“jdbcdatasource”));<br />      connection.setProperty(TaskList.CFG_DB_TYPE, sys.getAttribute(“dbtype”));<br />  <br />    } catch (NamingException ex) {<br />      throw new ConnectorException(ex);<br />    } catch (Exception e) {<br />      throw new ConnectorException(e);<br />    }</em></p><p>    tl.setConnectionConfiguration(connection);</p><p>    try {
      items = this.mapIdmToUwlItems(context, itemType, connectorFilter, system, tl.getTaskList());
      loc.infoT(items.toString());
    } catch (TaskListException e) {
      throw new ConnectorException(e);
    } catch (NamingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
   
    ProviderStatus status =
      new ProviderStatus(true, system, IdM_UWL_Test.CONNECTOR_ID);</p><p>    result =
      ConnectorResult.createSnapshotResult(
        new ItemCollection(items),
        status);
    return result;
  }</p><p>For nicer separation I also added an additional method – mapIdmToUwlItems() – which is used by the one above:</p><p>  private List mapIdmToUwlItems (UWLContext context, String itemType, ConnectorFilter qp, String system, List somItems) {
    List retItems = new ArrayList(); 
    ITask entry = null;     
    Item uwlItem = null;
     
    for(int j=0; j<somItems.size(); j+) {<br />      entry = (ITask)somItems.get(j);<br />      SimpleDateFormat sfd = new SimpleDateFormat(“yyyy-MM-dd’T’HH:mm:ss”);<br />      Date d = new Date();<br />      try {<br />        //Updated Nov 14th, 2008 in order to also work with version 7.1
        //IdM 7.0 – date field has no name in stored procedure result
        String dateString = (String)entry.getAttribute(“”);
        //IdM 7.1 – date field has name “PostedDate” in stored procedure result
        if (dateString == null) dateString = (String)entry.getAttribute(“PostedDate”);
        d = sfd.parse(dateString);
<br />      } catch (ParseException e) {<br />        e.printStackTrace();<br />      }<br />      <br />      uwlItem = new Item(IdMConnector.CONNECTOR_ID,   //connectorId<br />                system,              //systemId<br />                (String)entry.getAttribute(“AuditRef”),        //externalId<br />                context.getUserId(),  //userId<br />                Item.UNKNOWN_ATTACHMENT_EXISTENCE,   //attachment count<br />                d,      //date created<br />                (String)entry.getAttribute(“MSKEYVALUE”), //user MSKEYVALUE as creator id<br />                null,              //due date<br />                null,              //external object id<br />                myItemTypes[0],          //external type<br />                myItemTypes[0],          //item type<br />                PriorityEnum.getEnumFromInt(1),  //priority<br />                StatusEnum.NEW,//status<br />                (String)entry.getAttribute(“TaskName”) );  //subject<br />    <br />      uwlItem.setHollow(true);<br />      //Build the url for the approval items (updated Nov14th, 2008 in order to also work with version 7.1)
      String sysUrl;
      if (idm_version71) {
        sysUrl = idm_workflowUrl + “?TaskID=” + (String)entry.getAttribute(“TaskId”) + “&EntryID=” + (String)entry.getAttribute(“userMSKEY”) + “&RequestID=” + (String)entry.getAttribute(“AuditRef”);
      } else {
        sysUrl = idm_workflowUrl + “?mskey=” + (String)entry.getAttribute(“userMSKEY”)+ “&taskid=” + (String)entry.getAttribute(“TaskId”)+ “&auditref=” + (String)entry.getAttribute(“AuditRef”);
      }
      uwlItem.setExecutionUrl(sysUrl + urlParameters);
      uwlItem.setDescription(sysUrl + urlParameters);
      retItems.add(uwlItem);
    }

+    return retItems;               

  }</p><p>As you can see here, I only mapped some item properties. For example, I did not consider the due date so far.</p><p>Above methods make use of an additional class (TaskList) that implements a custom interface (ITask) plus a custom exception:</p><ul><li>TaskList </li><li>ITask</li><li>TaskListException</li></ul><p>  public interface ITask {
   
    public Object getAttribute(Object name);
   
    public void setAttribute(Object name, Object value);</p><p>  }</p><p>  public class TaskList {</p><p>    public static String CFG_DRIVER = “db.driver”;
    public static String CFG_JDBC_CONNECTION_STRING = “db.connectionstring”;
    public static String CFG_DB_USER = “db.user”;
    public static String CFG_DB_PASSWORD = “db.password”;
    public static String CFG_DB_USER_ATTRIBUTE = “db.user.attribute”;
    public static String CFG_DB_DATASOURCE = “db.datasource”;
    public static String CFG_DB_TYPE = “db.type”;
    public static String CFG_IDM_IDS_ID = “idm.ids.is”;</p><p>    // inner class
    class Task implements ITask {</p><p>      Map m_attributes;</p><p>      public Task() {
        this.m_attributes = new HashMap();
      }</p><p>      public Object getAttribute(Object name) {
        return this.m_attributes.get(name);
      }</p><p>      public void setAttribute(Object name, Object value) {
        this.m_attributes.put(name, value);
      }</p><p>      public boolean equals(Object o) {
        if (o instanceof Task) {
          return ((Task) o).m_attributes.equals(this.m_attributes);
        }
        return false;
      }</p><p>      public int hashCode() {
        return this.m_attributes.hashCode();
      }</p><p>      public String toString() {
        return this.m_attributes.toString();
      }</p><p>    }</p><p>    // member variables
    List m_list;
    boolean m_isBackendFetched;
    String m_userId;
    Properties m_config;</p><p>    public TaskList(String userLogonId) {
      this.m_list = new ArrayList(1);
      this.m_userId = userLogonId;
      this.m_config = new Properties();
      this.m_config.setProperty(TaskList.CFG_DB_USER_ATTRIBUTE, “MSKEYVALUE”);
    }</p><p>    public TaskList(String userLogonId, Properties configuration) {
      this(userLogonId);
      this.m_config = configuration;
    }</p><p>    public void setConnectionConfiguration(Properties p) {
      this.m_config.putAll(p);
    }</p><p>    public boolean isFetched() {
      return this.m_isBackendFetched;
    }</p><p>    public void setUnfetched() {
      this.m_isBackendFetched = false;
    }</p><p>    public List getTaskList() throws TaskListException, NamingException {
      if (this.isFetched())
        return this.m_list;</p><p>      if (this.m_config.size() == 0)
        throw new TaskListException(“Invalid configuration to connect to SAP IdentityManagement database.”);</p><p>      // fetch data from Identity Center
      Connection con = null;
      try {
        //retrieve data about AS Java JDBC data source and get a jdbc connection
        InitialContext ctx = new InitialContext();
        String lookupString = “jdbc/” + this.m_config.getProperty(TaskList.CFG_DB_DATASOURCE);
        DataSource ds = (DataSource) ctx.lookup(lookupString);
        con = ds.getConnection();</p><p>        if (con == null || con.isClosed())
          throw new TaskListException(“Connection to SAP IdentityManagement database not established.”);
       
        //translate logonId to MSKEY
        String m_idm_mskey = getUserMSKEY(con, this.m_userId);
       
        //retrieve approval tasks for current user
        this.getApprovalTasks(con, m_idm_mskey);</p><p>      } catch (SQLException e) {
        throw new TaskListException(e);
      } finally {
        //close connection
        if (con != null) try {
          con.close();
        } catch (SQLException e1) {
          throw new TaskListException(e1.getMessage());
        }
      }</p><p>      this.m_isBackendFetched = true;
      return this.m_list;
    }</p><p>    // in addition this class contains the three methods presented in the first part of the blog
    // – getMSKEYVALUEFromMSKEY()
    // – getUserMSKEY()
    // – getApprovalTasks()</p><p>
  public class TaskListException extends Exception {</p><p>    public TaskListException() {
      super();
    }</p><p>    public TaskListException(String arg0) {
      super(arg0);
    }</p><p>    public TaskListException(Throwable arg0) {
      super(arg0);
    }</p><p>    public TaskListException(String arg0, Throwable arg1) {
      super(arg0, arg1);
    }</p><p>  }+ JDBC Connector Service: http://help.sap.com/saphelp_nw70/helpdata/en/b0/6e62f30cbe9e44977c78dbdc7a6b27/frameset.htm

OpenSQL/JDBC: http://help.sap.com/saphelp_nw70/helpdata/en/57/1177611c11cd418564cdbc1488ce33/frameset.htm </li></ul>h3. Portal System Landscape Object 
<p>For maintaining all IdM connection properties through the portal system landscape you need to extend the property set of the IdM system object. You can do this by uploading your own system object using the following portalapp.xml file wrapped into a custom par.<br />Based on this par you create a new system object and maintain a system alias which you then later referce in your UWL connector.</p><p>+  <?xml version=”1.0″ encoding=”UTF-8″?><br />  <application><br />    <application-config><br />      <property name=”ServicesReference” value=””/><br />      <property name=”releasable” value=”true”/><br />    </application-config></p><p>    </p><p>          </p><p>          </p><p>          </p><p>          </p><p>          </p><p>      </p><p>     
     
   </p><p>    UWL: http://help.sap.com/saphelp_nw70/helpdata/EN/39/a1bb5c4c0d4ab4a417e87ef35f1efa/frameset.htm

UWL JavaDoc: http://help.sap.com/javadocs/NW04S/current/uw/index.html

     

    To report this post you need to login first.

    10 Comments

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

    1. Gobinath Kasimayan
      This is very good blog. It helped me lot.

      I am facing a problem after deploying the .par. I followed the blog(along with 1st blog). It is showing “No items found to display” along with warning message “An error occurred while trying to connect to the provider”. When I clicked on the details, “Exception type:java.lang.NullPointerException Message:null”.
      Scenerio: I am hardcoding(not fetching from db or any other source) all the value in “getItems” method.

      Please help me to solve this issue. Waiting for your reply.

      Thanks,
      Gobi

      (0) 
      1. Oliver Nocon Post author
        Hard to tell without your coding.
        You cannot copy the code 1:1.

        I would suggest that you debug your code in order to exactly see what’s going on. Do you have more details?

        (0) 
    2. Gobinath Kasimayan
      Hi,
      This is very good blog which helped me lot in creating a custom connector for third party application.

      I have a requirement wherein, I need to add some of the work items to Notification / Tracing. Is there any where custom development can be done on that.

      Please help me on this as this is very urgent.

      Thanks,
      Gobinath K

      (0) 
    3. Gobinath Kasimayan
      Hi,
      I want to inovke a method from custom connector when action (like complete/forward action) is clicked. Is there any possiblity of calling the connector method. If so please help me on this.

      Waiting for your response

      Thanks,
      Gobinath

      (0) 
        1. Gobinath Kasimayan
          Hi,

          Thanks for your reply.

          I have one more query. The work items are fetched from different applications. I need to sort the work items with respect to the application (sorting should not use table header sort).

          Please help me on this.

          Thanks,
          Gobi

          (0) 
          1. Oliver Nocon Post author
            as before, please address to the forum.
            I looked at the topic from the IdM perspective and was happy to get it working with UWL.
            I am no expert in UWL.
            (0) 
    4. Pavan Patil GC
      is that launching the external url (another web page to external website), i am trying similar but it is launching the new WD window not the external url, please find more info on this and can help me in this 
      /message/10352596#10352596 [original link is broken]
      (0) 

    Leave a Reply