LDAP exporter for HP OpenView ServiceDesk 4.5 & 5.x
version 2.00 [8th of October, 2008]
created by Radovan Skolnik, radovan@skolnik.info

Overview

This is a readme and a how-to file for LDAP export utility for HP OpenView ServiceDesk 4.5 & 5.x It produces XML file in the same format as the standard data exchange does (they call it CIM for some reason). However the possibilities in standard data exchange (regarding LDAP) are quite limited. That's why I have created this little tool. This tool is free. If it helps you achieve what you need any donations are highly welcome (click on the yellow button above).

Main features

Requirements

I have created this software using Java Software Development Kit (JDK) version 1.5. However it should run flawlessly using 1.7, 1.6, 1.4, 1.3 and probably 1.2 series. Java Runtime Environment (JRE) is sufficient - you do not need full Java SDK. It won't be able to run on Microsoft JVM. Please send me comments/issues regarding compatibility if you encounter any.

Download

You can download the whole archive with sample configuration file here

Usage

The usage is realy simple and same for all platforms:
java -jar SDLDAPExport.jar

In case you're traversing large directories you may also want to increase the memory available to Java runtime so that you do not run out of memory. This can be done like this (refer to Java docs about the switches):
java -Xms64m -Xmx1024m -jar SDLDAPExport.jar

I have added example.xml with some comments that shows most (or all) features available.

Description of configuration file

IMPORT_CONFIG

The whole configuration is enclosed in this tag that defines global parameters.
<IMPORT_CONFIG host="localhost" port="389" loginDN="" password="" outputFile="ldap.xml" logFile="ldap.log" timestampLDAPFormat="yyyyMMddHHmmss'Z'" timestampOutputFormat="dd/MM/yyyy HH:mm:ss" operationalAttributes="+" followReferrals="true" pageSize="0" useSSLTLS="false">

ENTRY_CLASS

The <IMPORT_CONFIG> tag can contain one or more <ENTRY_CLASS> tags. This tag contains a definition of a CIM class. For every LDAP entry retrieved one CIM class instance will be created.
<ENTRY_CLASS name="SD_ORGANIZATIONAL_UNIT" searchFilter="(objectclass=organizationalUnit)" searchScope="recursive" includeOperationalAttributes="true" searchBase="cn=intranetusers,dc=skolnik,dc=info" skipIf="" debug="true" maxEntries="0">

When exporter is run, it will connect to the server and find all LDAP entries that fulfill the searchScope/searchBase/filter criteria. Every record will be written as a 'name' class into output file (list of attributes is defined in <ATTRIBUTES> - see below). Here's a snippet from resulting XML showing tags resulting from <ENTRY_CLASS>:

    <VALUE.OBJECT>
    <INSTANCE CLASSNAME="SD_ORGANIZATIONAL_UNIT">
    ...
    </INSTANCE>
    </VALUE.OBJECT>
       
{one, sub, base} are standard scopes for LDAP searches (see some LDAP manual or Data Exchange guide from SD for explanation). 'recursive' is one of the main reasons for implementation of this tool. It will do standard 'one' search on defined searchBase but for every entry found it will recursively do search with same parameters - only searchBase will be replaced with DN of found entry. This allows recursive traversal of LDAP hierarchy (see <ENTRY_CLASS name="SD_ORGANIZATIONAL_UNIT"> in example.xml) <ENTRY_CLASS> can contain another <ENTRY_CLASS> tags (children). This will do a search from parent DN as searchBase using criteria in child <ENTRY_CLASS> (in example.xml this is used for finding Persons inside OUs).

For every entry all its attributes are fetched from server. If includeOperationalAttributes=true it also gets (otherwise invisible) operational attributes: You can override the list of operation attributes in <IMPORT_CONFIG> by specifying operationalAttributes.
The attributes received make up a 'variable namespace' for the current record - i.e. as if you declared variables with the names and values of LDAP attributes. Here's a sample from output log showing the situation:
    CLASS=SD_ORGANIZATIONAL_UNIT, ID=0
        modifiersName : java.lang.String = cn=intranetusers,dc=skolnik,dc=info
        creatorsName : java.lang.String = cn=intranetusers,dc=skolnik,dc=info
        ou : java.lang.String = General management
        objectClass : java.lang.String = organizationalUnit
        dn : java.lang.String = ou=General Management, cn=intranetusers,dc=skolnik,dc=info
        subschemaSubentry : java.lang.String = cn=Subschema
        modifyTimestamp : java.lang.String = 03/12/2004 09:05:51
        createTimestamp : java.lang.String = 03/12/2004 09:05:51
       
All the received variables are declared as Java String types. You can use these variables in <ATTRIBUTE> tags to create (Java) expressions that will be output to resulting CIM XML. The namespace also contains a special variable named $parent that holds a reference to a namespace of parent record - i.e.

ATTRIBUTES and ATTRIBUTE

Attributes written into output XML are defined within <ATTRIBUTES> tag. There can only be one <ATTRIBUTES> tag per <ENTRY_CLASS>. Inside <ATTRIBUTES> you put a set of <ATTRIBUTE>. Each will be evaluated and the result of evaluation put into current class as its attribute.
<ATTRIBUTES>
This is only a placeholder that holds actual <ATTRIBUTE> definitions and has no attributes.

There are two forms of how to define <ATTRIBUTE>. First one uses value attribute for definition of the expression:
<ATTRIBUTE name="searchcode" value="(sn + givenName.substring(0,1)).toUpperCase()" includeInOutput="true"/>

The second one uses <![CDATA[ and ]]> pair (no need to do XML escaping there) to enable you to write multi-line code instead of value attribute.

<ATTRIBUTE name="accountExpires" includeInOutput="true">
  <![CDATA[          
    import java.text.SimpleDateFormat; // yes we can freely import Java packages from runtime
    
    String getExpiryDate (long accountExpiresL) { // we write a function that will be used for evaluation, we do not have to care about exception handlers
      Calendar calendar = Calendar.getInstance();
      calendar.clear();
      calendar.set(1601, 0, 1, 0, 0);
      accountExpiresL = accountExpiresL/10000 + calendar.getTime().getTime();
      Date result = (new Date(accountExpiresL));
      SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); // same as timestampOutputFormat
      return sdf.format(result);
    }
    
    // result of this expression will be used (instead of "1234567890" you would use the AD variable holding the value (accountExpires probably))
    expiryDate = getExpiryDate((new Long("1234567890")).longValue()); 
  ]]>
</ATTRIBUTE>
      

Note that evaluation (variable names) is case sensitive. If you get some strange values where you do not expect them it can be caused by case-sensitiveness. If you find that a resulting value contains 'VOID' string, it means that 'value' in <ATTRIBUTE> tag contains reference to non-existent variable (most probably LDAP attribute not present in current record). In such cases use can set debug=true flag for problem class and check out a log file for exact attribute names and values. Also if you have attribute that sometimes is there and sometimes not, you would get 'VOID' values on occasions where it's missing from current LDAP record. You can use Java ternary expression in this form: (variable==void ? &quot;&quot; : variable) as a workaround. If the variable didn't exist it would be replaced by empty string (a pair of apostrophes - but as we're in XML we have to escape them as shown) otherwise its original value will be used.
The individual <ATTRIBUTE> tags are evaluated in the order they are written in configuration XML. This allows to define some attribute in one <ATTRIBUTE> and use it in more than one following tags.

Sample of CIM XML PROPERTY resulting from <ATTRIBUTE>.
    <VALUE.OBJECT>
    <INSTANCE CLASSNAME="SD_ORGANIZATIONAL_UNIT">
     <PROPERTY NAME="ID" TYPE="string">
      <VALUE>1</VALUE>
     </PROPERTY>

     <PROPERTY NAME="dn" TYPE="string">
      <VALUE>ou=Others, cn=intranetusers,dc=skolnik,dc=info</VALUE>
     </PROPERTY>

     <PROPERTY NAME="ou" TYPE="string">
      <VALUE>Others</VALUE>
     </PROPERTY>

    </INSTANCE>
    </VALUE.OBJECT>
       

ATTRIBUTE_CLASS

<ENTRY_CLASS> can also contain <ATTRIBUTE_CLASS> tags. The purpose of <ATTRIBUTE_CLASS> is to be able to create standalone class by going through array of values contained in attribute from its parent <ENTRY_CLASS>. Some attributes behave like array - i.e. one attribute has an array of values. For example objectclass contains many values, or attributes defining person's phone numbers or her/his belonging to some group are such examples. The <ATTRIBUTE_CLASS> can only have one <ATTRIBUTES> tag inside that holds attributes to be retrieved.
<ATTRIBUTE_CLASS name="SD_PHONE" expandOn="mobile" skipIf="mobile.startsWith("0")" debug="true">

See also

I have created more tools for HP OpenView ServiceDesk 4.5 & 5.x that I would like you to have a look at. Here is a list:

Thanks

History