2.4 - Adding entries

Adding entries is one of the base operations a user can do on an LDAP server. Nevertheless, it’s an operation that implies many checks, and frequently the user receives strange error messages. We will see how to add an entry using the LDAP API, and analyze the various error cases that can occur.

Adding an entry

Here is the simplest way to add an entry into the server, assuming that the entry is correct. In order to add an entry, you must provide the location where the entry is stored (its Dn) and the list of its Attributes contained within it.

Here are two examples where the entry is injected using LDIF:

    @Test
    public void testAddLdif1() throws Exception
    {
        connection.add(
            new DefaultEntry(
                "cn=testadd,ou=system", // The Dn
                "ObjectClass: top",
                "ObjectClass: person",
                "cn: testadd_cn",
                "sn: testadd_sn" ) );
        
        assertTrue( connection.exists( "cn=testadd,ou=system" ) );
    }

In this basic example, we are adding a new entry, created using some LDIF formatted parameters, the first one being the entry’s Dn.

Note that it is possible to use some variables in the LDIF instead of pure text. Here is the same example, resulting to the same entry being added:

    @Test
    public void testAddLdif2() throws Exception
    {
        String cn = "testadd_cn";
        String sn = "testadd_sn";
        
        connection.add(
            new DefaultEntry(
                "cn=testadd,ou=system", // The Dn
                "ObjectClass: top",
                "ObjectClass: person",
                "cn", cn, // Note : there is no ':' when using a variable
                "sn", sn ) );
        
        assertTrue( connection.exists( "cn=testadd,ou=system" ) );
    }

Down the line, what is important is that the add() operation is taking a full Entry.

We can also create the Entry in a different way, which is shown in the following paragraphs.

Sending an AddRequest

When more control is needed we ask the server to add an entry by sending an AddRequest, which allows a Control to be included in the request.

Here is an example (note that the control is just injected to demonstrate the feature, it doesn’t really do anything special in this example):

    @Test
    public void testAddWithControl() throws Exception
    {
        assertFalse( connection.exists( "cn=testadd,ou=system" ) );
        
        Entry entry = new DefaultEntry(
            "cn=testadd,ou=system",
            "ObjectClass : top",
            "ObjectClass : person",
            "cn: testadd_sn",
            "sn: testadd_sn" );
        
        AddRequest addRequest = new AddRequestImpl();
        addRequest.setEntry( entry );
        addRequest.addControl( new ManageDsaITImpl() );
        
        AddResponse response = connection.add( addRequest );
        
        assertNotNull( response );
        assertEquals( ResultCodeEnum.SUCCESS, response.getLdapResult().getResultCode() );
        
        assertTrue( connection.exists( "cn=testadd,ou=system" ) );
    }

Asynchronous addition

Sometimes we need to add an entry, but not check the result immediately. It’s just a matter of calling the addAsync() method, which returns a Future that can be checked somewhere else in the code:

    @Test
    public void testAddAsyncLdif() throws Exception
    {
        assertFalse( connection.exists( "cn=testAsyncAdd,ou=system" ) );
        
        Entry entry = new DefaultEntry(
            "cn=testAsyncAdd,ou=system",
            "ObjectClass: top",
            "ObjectClass: person",
            "cn: testAsyncAdd_cn",
            "sn: testAsyncAdd_sn" );
        
        AddRequest addRequest = new AddRequestImpl();
        addRequest.setEntry( entry );
        
        AddFuture addFuture = connection.addAsync( addRequest );
        
        // Here, we can do something else before checking that the entry has been added
        
        AddResponse addResponse = addFuture.get( 1000, TimeUnit.MILLISECONDS );
        
        assertNotNull( addResponse );
        assertEquals( ResultCodeEnum.SUCCESS, addResponse.getLdapResult().getResultCode() );
        
        assertTrue( connection.exists( "cn=testAsyncAdd,ou=system" ) );
    }

Do, Don’t

Successfully adding an entry assumes that the entry was correct, i.e. the attributes and values are compatible with the LDAP schema. There are many things checked by the server. Here is a list of constraints that you should respect in order to get your entry injected:

  • The entry must have at least one Structural ObjectClass
  • If the entry has more than one Structural ObjectClass, then they must be hierarchically related
  • The ObjectClasses define the list of allowed Structural AttributeTypes that can be used (MAY and MUST)
  • All the MUST AttributeTypes must be present
  • Each added value must follow the AttributeType Syntax
  • If the AttributeType is single valued, then you can’t add more than one value
  • The entry’s Dn must have a parent
  • You are not allowed as a user to inject operational attributes, unless they have the USER-MODIFICATION flag set to true.

There are also some other constraints, depending on the server, if it implements NameForms, DITStructureRules or DITContentRules.

Another reason an entry can be rejected is that there aren’t enough privilege to add it. You must ensure the LDAP server’s configuration allows you to add an entry in the correct location.

Errors

At first, you might expect to get an exception if the entry addition has failed. If the server is rejecting the addition, you will get NO exception*. Exceptions are only thrown client side if the entry is not built correctly, or if the connection is not opened.

In any other case, the server will simply return a LdapResult instance containing either SUCCESS or the cause of the rejection.

Usually, if you get an error while adding an entry, the message is hard to read. Most of the errors occur because the entry already exists, or because the entry has an LDAP schema violation.

The LdapResult in the response will provide helpful clues as to what the root cause is.