Oracle OID. Directory Synchronization Profiles, a minimalistic example with LDIF-files

Mattias Malmgren

2008-02-08
mattias@freefarm.se

This tutorial, including source code files can be downloaded from this zip-file: dip.zip

This document explains the basics of export/import Directory Synchronization Profiles to/from LDIF-files in Oracle Internet Directory version 10g (runing on windows XP).

There is good information on this topic on the internet. How ever, this is a very small example, and there for easy to understand. Also, the information I have found focus on synchronization with another direcotry (e.g. OpenLDAP), rather than LDIF-files using synchronization agents. A synchronization agent is a program you wirte yourself that converts information to/from a custom format that is not supported by the default built-in OID synchronization capabilities.

Links to information on this subject :

* Of the links above I especially recommend those marked with a star if you are a beginner

My test-environment

I use a PC with Windows-XP and have installed OID version 10g. I have used mostly default settings. (Especially I like the last prompt=# , makes me feel powerful ;-) ) You should be familiar with the basics of LDAP befor reading this.

Example 1, export synchronization profile

Steps in this example Note: If you have a directory that you want to synchronize with OID and that type of directory is supported by OID built-in synchronization (such as OpenLDAP, Microsoft Active Directory and so on, you should not use this LDIF-file approach).

Note: This example does not cover bootstrapping, which is the initial process of moving all information from the OID to a connected directory before the incremental synchronization of changes to the DIT begins.

Set up a DIT (Directory Information Tree)

This is a LDIF-file containing the DIT (if you don't understand the syntax, check LDIF Wikipedia):

dn: dc=gm dc: gm objectclass: top objectclass: domain dn: cn=John,dc=gm cn: John sn: Smith objectclass: top objectclass: person telephonenumber: 1

gm=the top domain for Gambia (a country in Africa, I picked it so it will hopfeully not collide with any other entrys in your OID). Copy and save the DIT into a file called dit.ldif in the directory c:\oidtst (all files should be saved here). Open a command prompt in the same direcotry as you saved the dit.ldif-file in and run the following command ( check out what the parameters mean in the reference. If you have installed your OID on a diffrent port than the default 389, change -p parameter to the port number of your installation. Same thing with the password -w, witch in my case is abc123.):

ldapadd -p 389 -h localhost -D "cn=orcladmin" -w abc123 -f dit.ldif

Using the Oracle Directory Manager Graphical user intercafe (from now on called the GUI) you should now see your new DIT.

Login:

Browse the DIT:

Note: If the GUI don't show anything at all, try to resize the window and make it a little bigger.

Create a synchronization profile

The synchronization profile is made of two files, the map-file and the properties-file. (While it is possible to create a synchronization profile using the GUI, I guess most real world applications will use the command-line approach. )

In the map-file we tell the OID what (and how) changes to the DIT shuld be exported to (in this case) a LDIF-file. The map-file has two parts, the domain rules and the attribute rules. Domain rules tells the OID which part of the DIT shoud be exported and the attribute rules tells the OID what and how attributes should be exporetd. This is my mapping file:

DomainRules dc=gm:dc=gm AttributeRules cn: : :person: telephonenumber: : :person

Copy and save this into a file called TestGmExp.map

To better understand the format, see reference and tutorial but basically, this is the format (src=source, dst=destination). Also, there are sample files in the default installation c:\OraHome_1\ldap\odi\samples\ ( C:\OraHome_1=ORACLE_HOME ).

DomainRules srcDomainName1: [dstDomainName1]: [DomainMappingRule1] AttributeRules srcAttrName1:[ReqAttrSeq]:[SrcAttrType]:[SrcObjectClass]:[dstAttrName1]:[DstAttrType]:[DstObjectClass]:[AttrMappingRule1]

In this simple export example most of the parameters can be omitted in the mapping file, it works anyway. Mappingfiles can e.g. change the name of a field, or add two fields into one. Also in can contain string manipulation so it can truncate fields etc. Se the Oracle documantation on this topic for further information.

Note, if the domainrules is writte like this instead:
DomainRules
dc=gm:NOLDAP
as some documentation suggests, the output in the exported LDIF-file will miss the dn, like this:

dn: cn=john,dc=gm changetype: MODIFY REPLACE: telephonenumber telephonenumber: 144233

Before makeing the properties-file we should find the OID's "last change number". Give this command:

ldapsearch -p 389 -h localhost -D "cn=orcladmin" -w abc123 -b "" -s base "objectclass=*" lastchangenumber

In my case that returns: lastchangenumber=5608. Fill that number in the properties-file on the row odip.profile.lastchgnum.

The properties-file contains information about the synchronization profile:

odip.profile.name = TestGmExp odip.profile.status = ENABLE odip.profile.syncmode = EXPORT odip.profile.retry = 1 odip.profile.schedinterval = 60 odip.profile.agentexecommand = C:\\j2sdk1.4.2_16\\bin\\java.exe -jar c:\\oidtst\\TestGmExp.jar odip.profile.interface = LDIF odip.profile.mapfile = c:/oidtst/TestGmExp.map odip.profile.lastchgnum = 5608 odip.profile.debuglevel = 63

In the samples directory (c:\OraHome_1\ldap\odi\samples\) you will find files that are commented to tell you what all these properties in the properties-file mean (I also posted this properties-file with comments). But briefly:

The odip.profile.agentexecommand tells the OID what program it shuld run after it has done an export. That program should, in one way or another, pass the information on to your other directory (or what you want to do with the exported information). In this case, we will just rename each exported file and give it a name with a timestamp. It is imporant that the final step of the agent is to delete the exported file. If the exported file is not deleted (or renamed) no more exports will take place.

Note: if you have Java installed somewhere else, which you problably have, you must change the path C:\\j2sdk1.4.2_16 to that of your installation of Java SDK. You should also put that path + bin in your PATH enviroment variable

This is my agent, written in Java, it simply renames the file and give it a name with a timestamp. After that, it cleans out older files every 10 minutes.

import java.io.*; import java.text.*; import java.util.*; public class TestGmExp { public static void main(String[] args) { String path="c:\\OraHome_1\\ldap\\odi\\data\\export\\"; Date now = new Date(); String timestamp = new SimpleDateFormat("yyyy_MM_dd-HH_mm_ss").format(now); File f = new File(path + "TestGmExp.dat"); f.renameTo(new File(path + "TestGmExp_"+timestamp+".dat")); // Since the command is executed ones evry 60 seconds, remove files // older than 10 minutes, or we will get a lot of files here... File dir = new File(path); String[] children = dir.list(); if (children == null) { return; } String tenminutes = timestamp.substring(0,"yyyy_MM_dd-HH_m".length()); for (int i=0; i<children.length; i++) { String filename = children[i]; if (filename.startsWith("TestGmExp_")) { if (!filename.startsWith("TestGmExp_"+tenminutes)) { File delfile = new File(path+filename); delfile.delete(); } } } } }

Save this into a file called TestGmExp.java (careful with the name, it is case sensitive even in windowsXP enviroment.

Compile it using the command:

javac TestGmExp.java

In order to avoid java classpath problems I pack this file in a jar-file along with a manifest-file so it will be an executable jar-file.

Main-Class: TestGmExp

Save that into a file called mainClass (note the 2 extra line feeds, file name is case sensitive). Then give this command to create an executable jar-file:

jar cfm TestGmExp.jar mainClass TestGmExp.class

Note: This agent is written in Java, but the agent need not to be a Java-program. It could be a bat-script or a PERL-program, etc. If you want a PERL-program and are on windows, use the full path to perl.exe and the PERL-program as an argument, like this for example:
c:\perl\bin\perl.exe c:\agent.pl

Now, upload the profile to the OID with this command:

dipassistant createprofile -h localhost -p 389 -D "cn=orcladmin" -w abc123 -f TestGmExp.properties -configset 1

Note the last parameter -configset 1. There should be a configset in the OID called 1. You can see it in the GUI if you browse to Server Management->Integration Server->Configuration Set 1.

If you click "Refresh" there you should see your profile in the list of profiles. You can also give this command:

ldapsearch -h localhost -D cn=orcladmin -w abc123 -b "orclodipagentname=TestGmExp,cn=subscriber profile,cn=changelog subscriber,cn=oracle internet directory" -s base "objectclass=*"

Now start the DIP-server using this command:

oidctl connect=mm server=odisrv instance=2 config=1 flags="refresh=1" start

See oracle documentation on this command, but briefly:

To test the profile, make a change to the telephonenumber. Create a ldif-file like this and save it into a file called changeJohnsTelephonenumber.ldif

dn: cn=john,dc=gm changetype: modify replace: telephonenumber telephonenumber: 2


Then give this command

ldapmodify -p 389 -h localhost -D "cn=orcladmin" -w abc123 -v -f changeJohnsTelephonenumber.ldif

You should be abel to see the change in the GUI (remeber to click refresh) or using a ldap search like this command:

# ldapsearch -h localhost -b "dc=gm" -s sub "cn=John" telephonenumber cn=John,dc=gm telephonenumber=2

Finaly, check files in the export-file directory, C:\OraHome_1\ldap\odi\data\export You should have a file there called something like TestGmExp_2008_02_06-16_28_05.dat witch contains the change like this:

dn: cn=john,dc=gm changetype: MODIFY REPLACE: telephonenumber telephonenumber: 2

Click refresh in configurationset 1 in the GUI and you should also be able to see in the status tab of the synchronization profile that the profile executed OK.

I suggest that you, after this, change status of this profile to "DISABLE" under the General-tab.

Error

If you get errors you can look for solutions in the trace-files in C:\OraHome_1\ldap\odi\log. Errors that I have encountered have bean "DATA_NOT_CONSUMED" as a status in the status-tab. That was caused by an error in the Java-program (the agent). Another error have bean NOT_EXECUTED_YET due to failure to start the DIP-server, or started it on wrong port number.



Example 2, import synchronization profile

Creating a import synchronization profile is very similar to a export profile. Therefor I will not explain the steps that arepretty much the same as careful as in the export example.

Create a new directory under c:\oidtst called import. Save all your files for this import-example in the new directory c:\oidtst\import

Mapping-file. Save it as TestGmImp.map :

DomainRules dc=gm:dc=gm AttributeRules cn: : :person:cn: :person: sn: : :person:sn: :person: telephonenumber: : :person :telephonenumber : :person :

Properties-file. Save it as TestGmImp.properties :

odip.profile.name = TestGmImp odip.profile.status = ENABLE odip.profile.syncmode = IMPORT odip.profile.agentexecommand = C:\\j2sdk1.4.2_16\\bin\\java.exe -jar c:\\oidtst\\import\\TestGmImp.jar odip.profile.retry = 1 odip.profile.schedinterval = 60 odip.profile.interface = LDIF odip.profile.mapfile = c:/oidtst/import/TestGmImp.map odip.profile.lastchgnum = 0 odip.profile.debuglevel = 63

Source code for the agent, save it into a file called TestGmImp.java

import java.io.*; public class TestGmImp { public static void main(String[] args) { try { String timestamp = new java.util.Date().toString().substring(11,8+11).replaceAll(":",""); FileWriter outFile = new FileWriter("c:\\OraHome_1\\ldap\\odi\\data\\import\\TestGmImp.dat"); PrintWriter out = new PrintWriter(outFile); out.println("dn: cn=john,dc=gm\n"+ "changetype: MODIFY\n"+ "REPLACE: telephonenumber\n"+ "telephonenumber: "+timestamp+"\n-\n\n" ); out.close(); } catch (IOException e){ e.printStackTrace(); } } }

Compile the java-program

javac TestGmImp.java

Create a mainClass file:

Main-Class: TestGmImp

Create an executable jar-file

jar -cfm TestGmImp.jar mainClass TestGmImp.class

Upload the profile:

dipassistant createprofile -h localhost -p 389 -D "cn=orcladmin" -w abc123 -f TestGmImp.properties -configset 1

Now, evrything seams to work fine. But it is not! When we check the telephonenumber it is not changed by the import-file. Oracle will not tolerate changes to the DIT (directory information tree in case you have forgot the acronym. If you check the log-file C:\OraHome_1\ldap\odi\log\TestGmImp.trc you will see an error message like this:

Exception Modifying Entry : javax.naming.NoPermissionException: [LDAP: error code 50 - Insufficient Access Rights]; remaining name 'cn=john,dc=gm' [LDAP: error code 50 - Insufficient Access Rights] javax.naming.NoPermissionException: [LDAP: error code 50 - Insufficient Access Rights]; remaining name 'cn=john,dc=gm' at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:2996)

We have to grant premissions to the agent to make updates to the OID's DIT. This can be done in two ways. Either with an ldif-file using ldapmodify, or with the GUI. The first approach is probably the best. It is documented in the tutorial, in Administrator's Guide and in Administrator's Guide Appendixes. How ever, I don't understand this documentation, so I do it with the GUI. This way I will grant more premissions than I want, but at this point I don't know any other way to do it. If you do, please send an email and tell me how!

If you now enable your export profile, you should be abel to see how the export file now also contains changes made by the import agent.