Accessing the Global Address List in MS Exchange from Java
Thursday October 27th 2005, 8:32 pm
Filed under: email,java,technology
Posted by: Andrew Lampert

Recently, I needed to get access to information stored in the Global Address List (GAL) in Microsoft Exchange, the address book that is commonly accessible as a corporate directory of staff through Outlook. I had a dig around on the interweb and although there are plenty of examples out there on using LDAP or accessing the list through the Outlook application via a Java-COM bridge, I couldn’t find anything that exactly explained how to access the MS Exchange GAL via LDAP. So for my benefit (and the off chance that this might help someone else out there) here’s how I did it.

Firstly, to work out which LDAP server to query, you can look at the configuration of your Outlook client (or of course, whatever other mail client you might use that’s hooked up to your corporate LDAP directory). For Outlook users, here are the steps to determine your current LDAP server:

  1. Open the Outlook address book
  2. Choose the Options … item from the Tools menu in the Address Book window. This pops up an Addressing window.
  3. Highlight the Global Address Book entry in the ordering panel at the bottom of the Addressing window.
  4. Click the Properties button to see the properties for the Global Address List. This window shows the "Microsoft Exchange Address Book Provider", which specifies the address of the current LDAP server.

The Microsoft Exchange Server administrator creates and maintains this Global Address List (GAL). The GAL contains information for every email user, as well as details of global distribution lists and public folder e-mail addresses. Note that (as far as I’m aware) there is no standard naming for the properties in each GAL entry, so some knowledge of the specific GAL entry format for your organisation is required. In my case, I used the freely available Java LDAP Browser/Editor to browse the some entries for people in the CSIRO GAL to understand how the relevant properties were stored (in my case firstname, surname and email address).

With this information in hand, we can now use the JNDI API to access the LDAP directory. My Java code for this is shown below. Note that "ident" is CSIRO parlance for userId or username. This code returns a list with the user’s firstname, surname and email address from the user’s entry in the GAL.

Note that in the code below, WordPress has escaped all quote characters.

package scifly.ldap;

import myriad.tools.Trace;

import javax.naming.*;
import javax.naming.directory.*;
import java.util.*;

/**
 * Title: IdentResolver
 * Description:
 *
 * This class is a generic class for connecting to the CSIRO
 * LDAP directory (specifically, the Global Address List
 * (GAL) in MS Exchange) and resolving a CSIRO ident to
 * a first name, surname and main email address.
 *
 * Creation Date: 14/10/2005 Creation Time: 17:14:11
 *
 * Copyright CSIRO 2005
 *
 * @author Andrew Lampert
 * @version $Id: IdentResolver.java,v 1.2 2005/10/18
 *     01:36:21 andrew Exp $
 */
public class IdentResolver {
    private DirContext ctx;
    // The LDAP directory server
    private String LDAP_SERVER="ldap://ldapserver.sgi.nu:389";
    // The username you use to connect to the LDAP directory
    private String LDAP_ID;
    // The password used to access the LDAP directory
    private String LDAP_CREDENTIAL;

    public IdentResolver(String _id, String _credential) {
        try {
            LDAP_ID=_id;
            LDAP_CREDENTIAL=_credential;
            connect();
        }
        catch (NamingException e) {
            Trace.error("NamingException occured while "+
                   "attempting to connect to LDAP directory: "+e);
            e.printStackTrace();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void shutdown() {
        try {
            ctx.close();
        }
        catch (NamingException e) {
            Trace.error("ERROR Closing Context connection: "+e);
            e.printStackTrace();
        }
    }

    /**
     * Connect to the LDAP directory
     * @throws NamingException
     */
    private void connect() throws NamingException {
        Hashtable env = new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,
                      "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, LDAP_SERVER);
        env.put(Context.SECURITY_AUTHENTICATION,
                      "simple");
        env.put(Context.SECURITY_PRINCIPAL, LDAP_ID);
        env.put(Context.SECURITY_CREDENTIALS,
                      LDAP_CREDENTIAL);
        ctx = new InitialDirContext(env);
    }

    /**
     * If the connection times out, use this method to
     * reconnect to the LDAP server without needing
     * to reauthenticate.
     */
    public void reconnect() throws NamingException {
        shutdown();
        connect();
    }

    /**
     * Lookup a specific ident
     * @param _ident
     * @throws NamingException If there is a problem
     * accessing the LDAP directory
     * @throws IdentNotFoundException If the specified
     * ident does not exist in the current LDAP directory
     */
    public List searchIdent(String _ident) throws
                               NamingException, IdentNotFoundException {
        List returnList = new ArrayList();

        SearchControls ctls = new SearchControls();
        ctls.setReturningObjFlag (true);
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        String filter = "(cn="+_ident+")";
        NamingEnumeration answer =
            ctx.search("OU=People,DC=YourOrganisation", filter, ctls);
        while(answer.hasMore()) {
            SearchResult item = (SearchResult) answer.next();
            Attributes attr = item.getAttributes();

            // Get the Given Name
            returnList.add(attr.get("givenName").get());
            // Get the Surname
            returnList.add(attr.get("sn").get());
            // Get the primary email address
            returnList.add(attr.get("mail").get());

            return returnList;
        }
        Trace.all("No details found for "+_ident);
        throw new IdentNotFoundException("IdentResolver ERROR: "+
                "The ident '"+_ident+"' was not found in " +
                "the current LDAP directory '"+LDAP_SERVER+"'.");
    }

    /**
     * Allow the current LDAP server address to be retrieved.
     * @return The address of the current LDAP server
     */
    public String getLDAP_SERVER() {
        return LDAP_SERVER;
    }

    /**
     * Allow a specific LDAP server to be used for lookups
     * @param LDAP_SERVER The full protocol address of
     * a valid LDAP server
     * (e.g.: ldap://ldapserver.sgi.nu:389)
     */
    public void setLDAP_SERVER(String LDAP_SERVER) {
        this.LDAP_SERVER = LDAP_SERVER;
    }
}

Hope this is helpful for someone! Please leave any comments or questions you have.


32 Comments so far
Leave a comment

This is very helpful article….
But i think that some things shuold be elaborated in more way as what are the minimal parameters needed for the connecting the LDAP Directory.
But anyways, this is really helpful for me.
Thanks and regards,
Amit

Comment by Amit 03.01.06 @ 3:03 pm

Thanks Amit,

Not sure exactly what you mean by the minimum parameters. I assume you might mean those in the connect() method? I believe all those parameters are required or at least desirable. Of course, different providers may exhibit specific behaviour in the absence of one or more of those properties, but I wouldn’t like to rely on that behaviour. I believe it’s better to be explicit.

You can, of course, set those attributes for the LDAP connection via properties (e.g., java.naming.security.credentials for the Context.SECURITY_CREDENTIALS value, java.naming.factory.initial for the INITIAL_CONTEXT_FACTORY value).

Regards,
Andrew

Comment by Andrew Lampert 03.01.06 @ 3:36 pm

Hi Andrew,

I am trying to access the Address Book component in Outlook 2003 and I have gone through your article. In Outlook 2003 when I have opend the Global Address Book properties, I see the following “C:\Documents and Settings\psarigal\Local Settings\Application Data\Microsoft\Outlook\” under the Microsoft Exchange Address Book Provider. I dont see any LDAP server setting there. Can you let me know if your program works with Outlook 2003?

Thanks in advance,
Praveen

Comment by Praveen 11.15.06 @ 1:55 am

Hi Praveen,

I’m actually using Outlook 2002 still, so I’m not sure if Outlook 2003 has changed this setting (but I would be somewhat surprised).

One thing to check is whether you are using Outlook in Connected mode or Offline mode. In Offline mode, the address book is cached, and the properties will indeed show its server as a file in the local filesystem. Assuming you have an Exchange server and are running in Connected mode, then, as far as I’m aware, you should see the LDAP server settings there.

If you still see a local filesystem location during Connected operation, then I’m not sure what to suggest. Does anyone else have any suggestions?

Cheers,
Andrew

Comment by Andrew Lampert 11.15.06 @ 8:16 am

Hi Andrew,

Thanks for pointing me in the right direction. As you have rightly mentioned I was connecting to Exchange server in offline mode. Once changing that to online mode, I was able to get the LDAP server info.

This posting really helps java programmers who want to connect to MS Outlook, without much hassle of COM interoperability.

Thanks,
Praveen

Thanks

Comment by Praveen 11.15.06 @ 8:49 pm

Hi Andrew,

I’m currently having issues with connecting to the exchange server. When i call the function IdentResolver, I get a bind error. Can you help?

Thanks

Comment by Shawn 03.21.07 @ 8:23 am

Hi Shawn,

Make sure you’re connecting to your LDAP server, rather than your exchange server (which is sounds like you might be doing?) Also, make sure that you’re using correct username and credentials.

Beyond these reasonably obvious points, can you post details of the Bind Error (like a stack trace)? This would help diagnose things further.

Cheers,
Andrew

Comment by Andrew Lampert 03.29.07 @ 3:58 pm

Is it possible to read email from exchange server using this technique?
I don’t have IMAP or POP3 or Outlook Web access enabled on my exchange server

Comment by shiv 10.16.07 @ 5:15 pm

Hi Shiv,

No – this post is only about accessing data from an LDAP directory, not about accessing email content.

If you don’t have IMPA, POP3 or OWA access to your Exchange server, then MAPI is your only choice. Access to Exchange via MAPI is tricky from Java (which it sounds like you need), but much easier if you’re happy to use .NET .

The only Java solution I’m aware of is the commercial offering by J-Integra (See http://j-integra.intrinsyc.com/support/exchange/doc/ for details). This is, however, a pricey alternative.

Hope this helps.

Cheers,
Andrew

Comment by Andrew Lampert 10.19.07 @ 4:52 pm

Hi Andrew,
A very helpful article. I have been looking to get this exact information since last 2 days and was delighted to find this.

Thanks again.

Comment by Kshitiz 12.09.08 @ 8:54 pm

Hi Kshitiz,

Glad to hear it was helpful for you!

Cheers,
Andrew

Comment by Andrew Lampert 12.11.08 @ 6:18 pm

Excellent article. Very Helpful

Comment by gerrard 02.05.09 @ 7:16 am

I find this as a very helpfull article.
Thanks.

Comment by ganesh 08.31.09 @ 9:56 pm

Glad to hear that people are still finding this article useful!

Comment by Andrew Lampert 09.01.09 @ 2:30 pm

Thanks a ton Andrew!

This information is very useful. I want to go one more level down. I want to read user’s personal contacts. Do you know if personal contacts are also stored in Exchange LDAP? We have form to request user’s exchange id and mail password; once user supplies his/her credentials, I want to connect to ldap using user’s credentials to get Global Address List entries and user’s contacts as well. Do you think this is possible?

Thanks,
JT

Comment by JT 01.13.10 @ 1:43 pm

Hi JT,

Not sure about accessing contacts – they’re certainly stored in Exchange, but not sure that they’re LDAP accessible.

I haven’t used j-XChange, but perhaps it might be helpful? It supports Exchange interaction from Java without CDO or MAPI.

Let me know if you find a solution.

Thanks,
Andrew

Comment by Andrew Lampert 01.13.10 @ 3:24 pm

Thanks Andrew!

You are right. I asked Exchange person and he mentioned that personal contacts are stored in the mailbox on Exchange and not in AD. Therefore, as you said, I would need to use something like j-XChange.

Regards,
JT

Comment by JT 01.20.10 @ 1:07 am

Thanks this has been extremely helpful. But I have one issue, the LDAP query returns only row for every query that I have tried including wild characters as well. Any idea ?

Comment by M T 08.13.10 @ 6:07 am

Hi M T,

Not sure exactly what you’re trying to search for, but if you want to find a search that matches multiple LDAP entries, it’ll just depend on configuring your search appropriately. In my case, I (almost) always want a single result back – given that I’m searching on a unique organisational identifier. When you’re calling ctx.search(“OU=People,DC=YourOrganisation”, filter, ctls), you’ll want to configure your search space, search controls and filter according to the result you want.

Thanks,
Andrew

Comment by Andrew Lampert 08.16.10 @ 11:31 am

Hi Andrew,
Im not able to see the LDAP server. I see D:\LocalData\s730042\Outlook\ under “Microsoft Exchange Address Book Provider”. Im using Outlook 2003 and am working in Connected mode. Any idea why?

Comment by Nimi 09.07.10 @ 1:41 pm

Hi Nimi,

I’m not sure why you’re seeing a local file location when working in connected mode. The only thing I can think of is perhaps you’re using Cached Exchange Mode to access your Exchange server? I’m not sure, but perhaps in this case the address book is also cached on the local file system.

Nimi, are you running in cached exchange mode? Otherwise, does anyone else have any ideas?

Thanks,
Andrew

Comment by Andrew Lampert 09.08.10 @ 10:26 am

Hi Andrew,
My Outlook is connected. It shows as Connected at the bottom right hand corner. I don’t know if its in the Cached Exchange Mode. How do I find it?

Comment by Nimi 09.08.10 @ 7:57 pm

Hi Nimi,

Reading a bit more, it seems Outlook does indeed use the Offline Address Book (OAB) when in cached exchange mode, so I suspect that’s the source of your issue.

You can find information about how to configure and enable/disable cached exchange mode for Outlook 2003 on the Microsoft Office site.

Cheers,
Andrew

Comment by Andrew Lampert 09.08.10 @ 8:28 pm

I am getting error at the following line:

import myriad.tools.Trace

From where do I get the myriad.tools package????

Comment by Avtar 03.31.11 @ 5:56 pm

Hi Avtar,

You can ignore the myriad.tools.Trace usage and import. It’s just a custom logging class from a project whose source code is not public. Go ahead and replace all method calls to Trace.*() with appropriate method calls on java.util.logging.Logger (or even System.out.println or System.err.println) instead.

Cheers,
Andrew

Comment by Andrew Lampert 04.05.11 @ 4:05 pm

Hi Andrew,
I am getting this error while executing above code any suggestion will be helpful.
javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 525, vece

I tried on google but did not find any good solution, I am sending u my code please go through it and suggest.
Thanks in advance
public class OutLookAccess {

private DirContext ctx;
// The LDAP directory server
//private String LDAP_SERVER = "ldap://ldapserver.sgi.nu:389";
private String LDAP_SERVER = "ldap://NWSRV01.nw.internal:389";
// The username you use to connect to the LDAP directory
private String LDAP_ID;
// The password used to access the LDAP directory
private String LDAP_CREDENTIAL;

public OutLookAccess(String _id, String _credential) {
try {

LDAP_ID = _id;

LDAP_CREDENTIAL = _credential;
connect();
} catch (NamingException e) {
/*
* Trace.error("NamingException occured while "+
* "attempting to connect to LDAP directory: "+e);
*/
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public OutLookAccess(){

}

public void shutdown() {
try {
System.out.println("shutdown()");
ctx.close();
} catch (NamingException e) {
// Trace.error("ERROR Closing Context connection: "+e);
e.printStackTrace();
}
}

/**
* Connect to the LDAP directory
*
* @throws NamingException
*/
private void connect() throws NamingException {

Hashtable env = new Hashtable();
try {

env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, LDAP_SERVER);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, LDAP_ID);
//env.put(Context.SECURITY_PRINCIPAL,"uid=tapan,ou=First Administrative Group,ou=Melbourne,dc=ost,dc=netwealth,dc=com");

env.put(Context.SECURITY_CREDENTIALS, LDAP_CREDENTIAL);
System.out.println("starting::::444");
ctx = new InitialDirContext(env);
System.out.println("starting::::44X");
} catch(Exception e){
e.printStackTrace();
}
}

/**
* If the connection times out, use this method to reconnect to the LDAP
* server without needing to reauthenticate.
*/
public void reconnect() throws NamingException {
System.out.println("reconnect()");
shutdown();
connect();
}

/**
* Lookup a specific ident
*
* @param _ident
* @throws NamingException
* If there is a problem accessing the LDAP directory
* @throws IdentNotFoundException
* If the specified ident does not exist in the current LDAP
* directory
*/
public List searchIdent(String _ident) throws NamingException, Exception {//, IdentNotFoundException {
System.out.println("starting::::5555");
List returnList = new ArrayList();
SearchControls ctls = new SearchControls();
try {
ctls.setReturningObjFlag(true);
ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(cn=" + _ident + ")";
NamingEnumeration answer = ctx.search("OU=People,DC=netwealth Investment", filter, ctls);
//NamingEnumeration answer = ctx.search("DC=ldap://NWSRV01.nw.internal:389",filter, ctls);

while (answer.hasMore()) {
SearchResult item = (SearchResult) answer.next();
Attributes attr = item.getAttributes();

// Get the Given Name
returnList.add((Attributes) attr.get("givenName").get());
// Get the Surname
returnList.add((Attributes) attr.get("sn").get());
// Get the primary email address
returnList.add((Attributes) attr.get("mail").get());

}

} catch (Exception e){
System.out.println("ident::: "+_ident+ " ::Ldap server:: "+LDAP_SERVER);
e.printStackTrace();
}
return returnList;
// Trace.all("No details found for "+_ident);
/*throw new Exception("IdentResolver ERROR: "
+ "The ident '" + _ident + "' was not found in "
+ "the current LDAP directory '" + LDAP_SERVER + "'.");*/
}

/**
* Allow the current LDAP server address to be retrieved.
*
* @return The address of the current LDAP server
*/
public String getLDAP_SERVER() {
System.out.println("starting::::000");
return LDAP_SERVER;
}

/**
* Allow a specific LDAP server to be used for lookups
*
* @param LDAP_SERVER
* The full protocol address of a valid LDAP server (e.g.:
* ldap://ldapserver.sgi.nu:389)
*/
public void setLDAP_SERVER(String LDAP_SERVER) {
System.out.println("starting::::XXXX");
this.LDAP_SERVER = LDAP_SERVER;
}

/**
* @param args
*/
public static void main(String[] args) {
OutLookAccess outLook = new OutLookAccess(“tupadhay”,”Interglobe01″);

}

}

Comment by Tapan 09.29.11 @ 4:19 pm

Hi Andrew,

First of all thanks alot for the simple and easily understandable tutorial :) . I have two questions to you. 1) How to get the list of all the users and there email id’s from the organizations GAL?
2) I followed the tutorial and whenever i am calling the searchIdent() i am getting the below error. Please guide me through this, i am new to Java Naming directory thing. Thanks alot, appreciate your help.

javax.naming.PartialResultException: [LDAP: error code 10 – 0000202B: RefErr: DSID-03100768, data 0, 1 access points
ref 1: ‘yourorganisation’
remaining name ‘OU=People,DC=YourOrganisation’
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2903)
at com.sun.jndi.ldap.LdapCtx.processReturnCode(LdapCtx.java:2820)
at com.sun.jndi.ldap.LdapCtx.searchAux(LdapCtx.java:1829)
at com.sun.jndi.ldap.LdapCtx.c_search(LdapCtx.java:1752)
at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_search(ComponentDirContext.java:368)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:338)
at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.search(PartialCompositeDirContext.java:321)
at javax.naming.directory.InitialDirContext.search(InitialDirContext.java:248)
at com.ldap.gal.IdentResolver.searchIdent(IdentResolver.java:104)
at com.ldap.gal.IdentResolver.main(IdentResolver.java:149)

Comment by jack sparrow 10.01.11 @ 7:03 am

Hi Tapan,

public static void main(String[] args) {
OutLookAccess outLook = new OutLookAccess(“tupadhay”,”Interglobe01″);

**In the above code, use your complete emailId with domain. Ex:tupadhay@yahoo.com

Comment by jack sparrow 10.01.11 @ 7:14 am

Hi Jack,
Thanks for advice,
i tride
OutLookAccess outLook = new OutLookAccess(“tapan@netwealth.com.au”,”Interglobe01″);

but its not able to read the global list, its showing following error.
———–

javax.naming.AuthenticationException: [LDAP: error code 49 – 80090308: LdapErr: DSID-0C090334, comment: AcceptSecurityContext error, data 525, vece
———–
i think there is something wrong i did when trying to access “ctx = new InitialDirContext(env);” code,
please help.
Thanks.

Comment by Tapan 10.04.11 @ 3:41 pm

Hi,
Question. Is it possible to get all the names from one specific email distro? If yes where should I put the email distro name in the code? by the way you do it now is it just pull up the person in address book in your outlook?
thank you

Comment by Yi 11.03.11 @ 6:37 am

Excellent read. I just passed this onto a buddy who was doing some research on that. He actually bought me lunch since I found it for him! So let me rephrase: Thanx for lunch!

Comment by hgh energizer supplements 04.03.12 @ 11:58 pm

Hi
Thanks for ur simple tutorial.It is very helpful.I tried your code .But i got error
javax.naming.CommunicationException: ldapserver:389 [Root exception is java.net.ConnectException: Connection timed out: connect]

I got the server dtail from

AddressBook>Tools>Options>properties

But I did n’t get the port details

LDAP_SERVER=”ldap://my-ldapserver:389″;

But I got the error.Can you advice how to trouble shoot this issue?

Comment by Neemisha 04.13.12 @ 4:18 pm



Leave a comment
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

(required)

(required)