From c2d6efe56f08399d12f76ea3a31bd5cb885b6caa Mon Sep 17 00:00:00 2001
From: Mark Craig <mark.craig@forgerock.com>
Date: Fri, 01 Mar 2013 17:09:07 +0000
Subject: [PATCH] CR-1327 Fix for OPENDJ-772: Document how to set up the REST to LDAP Servlet

---
 opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-rest2ldap.xml        |  513 +++++++++++++++++++++++
 opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-whats-new.xml          |    8 
 opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-standards.xml        |   27 +
 opendj-sdk/opendj3/src/main/docbkx/admin-guide/index.xml                     |    2 
 opendj-sdk/opendj3/src/main/docbkx/install-guide/chap-install-cli.xml        |   82 +++
 opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-rest-operations.xml      |  612 +++++++++++++++++++++++++++
 opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-listeners.xml            |   58 ++
 opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-before-you-install.xml |    4 
 8 files changed, 1,303 insertions(+), 3 deletions(-)

diff --git a/opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-rest2ldap.xml b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-rest2ldap.xml
new file mode 100644
index 0000000..280c699
--- /dev/null
+++ b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-rest2ldap.xml
@@ -0,0 +1,513 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ! CCPL HEADER START
+  !
+  ! This work is licensed under the Creative Commons
+  ! Attribution-NonCommercial-NoDerivs 3.0 Unported License.
+  ! To view a copy of this license, visit
+  ! http://creativecommons.org/licenses/by-nc-nd/3.0/
+  ! or send a letter to Creative Commons, 444 Castro Street,
+  ! Suite 900, Mountain View, California, 94041, USA.
+  !
+  ! You can also obtain a copy of the license at
+  ! trunk/opendj3/legal-notices/CC-BY-NC-ND.txt.
+  ! See the License for the specific language governing permissions
+  ! and limitations under the License.
+  !
+  ! If applicable, add the following below this CCPL HEADER, with the fields
+  ! enclosed by brackets "[]" replaced with your own identifying information:
+  !      Portions Copyright [yyyy] [name of copyright owner]
+  !
+  ! CCPL HEADER END
+  !
+  !      Copyright 2013 ForgeRock AS
+  !
+-->
+<appendix xml:id='appendix-rest2ldap'
+          xmlns='http://docbook.org/ns/docbook' version='5.0' xml:lang='en'
+          xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
+          xsi:schemaLocation='http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd'
+          xmlns:xlink='http://www.w3.org/1999/xlink'
+          xmlns:xinclude='http://www.w3.org/2001/XInclude'>
+ <title>REST LDAP Gateway Configuration</title>
+ <indexterm><primary>REST LDAP gateway</primary></indexterm>
+ <!-- This belongs in an OpenDJ reference. Ultimately this doc should
+      be generated, too, rather than written by hand. CREST-71? -->
+
+ <para>The OpenDJ REST LDAP gateway runs as a Servlet independent from your
+ directory service. You configure the gateway to access your directory service
+ by editing <filename>opendj-rest2ldap-servlet.json</filename> where you deploy
+ the gateway web application.</para>
+
+ <variablelist>
+  <para>The JSON format configuration can hold the following configuration
+  objects.</para>
+
+  <varlistentry>
+   <term>"primaryLDAPServers" (required)</term>
+   <listitem>
+    <para>The gateway accesses this array of LDAP servers before failing over
+    to the secondary LDAP servers. These might be LDAP servers in the same
+    data center for example.</para>
+
+    <programlisting language="javascript">{
+    "primaryLDAPServers": [
+        {
+            "hostname": "local1.example.com",
+            "port": 1389
+        },
+        {
+            "hostname": "local2.example.com",
+            "port": 1389
+        }
+    ]
+}</programlisting>
+
+    <para>By default, the gateway connects to the directory server listening
+    on port 1389 on the local host.</para>
+   </listitem>
+  </varlistentry>
+
+  <varlistentry>
+   <!-- TODO: change when we get more authentication options. -->
+   <term>"authentication" (required)</term>
+   <listitem>
+    <para>The gateway authenticates by simple bind using the credentials
+    specified.</para>
+
+    <programlisting language="javascript">{
+    "authentication": {
+        "bindDN": "cn=Directory Manager",
+        "password": "password"
+    }
+}</programlisting>
+   </listitem>
+  </varlistentry>
+
+  <varlistentry>
+   <term>"mappings"</term>
+   <listitem>
+    <para>For each gateway collection URI such as <literal>/users</literal> and
+    <literal>/groups</literal>, you configure a mapping between the JSON
+    resource returned by the gateway, and the LDAP entry returned by the
+    directory service.</para>
+
+    <variablelist>
+     <para>Each mapping has a number of configuration elements.</para>
+
+     <varlistentry>
+      <term>"baseDN" (required)</term>
+      <listitem>
+       <para>The base DN where LDAP entries are found for this mapping.</para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>"attributes" (required)</term>
+      <listitem>
+       <para>How the JSON resource fields map to attributes on LDAP
+       entries, each taking the form "<replaceable>field-name</replaceable>":
+       <replaceable>mapping-object</replaceable>. A number of
+       <replaceable>mapping-object</replaceable>s are supported.</para>
+
+       <variablelist>
+        <varlistentry>
+        <term>"constant"</term>
+        <listitem>
+         <para>Maps a single JSON attribute to a fixed value.</para>
+
+         <para>This can be useful as in the default case where each JSON
+         resource "schemas" takes the SCIM URN, and so the value is not related
+         to the underlying LDAP entries.</para>
+
+         <programlisting language="javascript">{
+    "schemas": {
+        "constant": [
+            "urn:scim:schemas:core:1.0"
+        ]
+    }
+}</programlisting>
+        </listitem>
+       </varlistentry>
+
+       <varlistentry>
+        <term>"simple"</term>
+        <listitem>
+         <para>Maps a JSON field to an LDAP attribute.</para>
+
+         <para>Simple mappings are used where the correspondence between JSON
+         fields and LDAP attributes is one-to-one.</para>
+
+         <programlisting language="javascript">{
+    "userName": {
+        "simple": {
+            "ldapAttribute": "mail",
+            "isSingleValued": true,
+            "writability": "readOnly"
+        }
+    }
+}</programlisting>
+
+         <itemizedlist>
+          <para>Simple mappings can take a number of fields.</para>
+
+          <listitem>
+           <para>(Required) "ldapAttribute": the name of LDAP attribute.</para>
+          </listitem>
+
+          <listitem>
+           <para>(Optional) "defaultValue": the JSON value if no LDAP attribute
+           is available on the entry.</para>
+          </listitem>
+
+          <listitem>
+           <para>(Optional) "isBinary": true means the LDAP attribute is
+           binary and the JSON field gets the base64-encoded value.</para>
+          </listitem>
+
+          <listitem>
+           <para>(Optional) "isRequired": true means the LDAP attribute is
+           mandatory and must be provided to create the resource.</para>
+          </listitem>
+
+          <listitem>
+           <para>(Optional) "isSingleValued": true means represent a possibly
+           multi-valued LDAP attribute as a single value, rather than an array
+           of values.</para>
+          </listitem>
+
+          <listitem>
+           <para>(Optional) "writability": indicates whether the LDAP attribute
+           supports updates. This field can take the following values.</para>
+
+           <itemizedlist>
+            <listitem>
+             <para>"createOnly": This attribute can be set only when the
+             entry is created. Attempts to update this attribute thereafter
+             result in errors.</para>
+            </listitem>
+            <listitem>
+             <para>"createOnlyDiscardWrites": This attribute can be set only
+             when the entry is created. Attempts to update this attribute
+             thereafter do not result in errors. Instead the update value
+             is discarded.</para>
+            </listitem>
+            <listitem>
+             <para>"readOnly": This attribute cannot be updated. Attempts to
+             update this attribute result in errors.</para>
+            </listitem>
+            <listitem>
+             <para>"readOnlyDiscardWrites": This attribute cannot be updated.
+             Attempts to update this attribute do not result in errors. Instead
+             the update value is discarded.</para>
+            </listitem>
+            <listitem>
+             <para>"readWrite": This attribute can be set at creation and
+             updated thereafter.</para>
+            </listitem>
+           </itemizedlist>
+          </listitem>
+         </itemizedlist>
+        </listitem>
+       </varlistentry>
+
+       <varlistentry>
+        <term>"object"</term>
+        <listitem>
+         <para>Maps a JSON object to LDAP attributes.</para>
+
+         <para>This mapping lets you create JSON objects whose fields themselves
+         have mappings to LDAP attributes.</para>
+        </listitem>
+       </varlistentry>
+
+       <!-- More to come?
+       <varlistentry>
+        <term></term>
+        <listitem>
+         <para></para>
+        </listitem>
+       </varlistentry>
+       -->
+       </variablelist>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>"namingStrategy" (required)</term>
+      <listitem>
+       <para>The approach used to map LDAP entry names to JSON resources. One
+       of the following.</para>
+
+       <itemizedlist>
+        <listitem>
+         <para>RDN and resource ID are both derived from a single user attribute
+         in the LDAP entry, as in the following example, where the
+         <literal>uid</literal> attribute is the RDN and its value is the
+         JSON resource ID.</para>
+
+         <programlisting language="javascript">{
+    "namingStrategy": {
+        "strategy": "clientDNNaming",
+        "dnAttribute": "uid"
+    }
+}</programlisting>
+        </listitem>
+
+        <listitem>
+         <para>RDN and resource ID are derived from separate user attributes in
+         the LDAP entry, as in the following example where the RDN attribute is
+         <literal>uid</literal> but the JSON resource ID is the value of the
+         <literal>mail</literal> attribute.</para>
+
+         <programlisting language="javascript">{
+    "namingStrategy": {
+        "strategy": "clientNaming",
+        "dnAttribute": "uid",
+        "idAttribute": "mail"
+    }
+}</programlisting>
+        </listitem>
+
+        <listitem>
+         <para>RDN is derived from a user attribute and the resource ID from an
+         operational attribute in the LDAP entry, as in the following example,
+         where the RDN attribute is <literal>uid</literal> but the JSON resource
+         ID is the value of the <literal>entryUUID</literal> operational
+         attribute.</para>
+
+         <programlisting language="javascript">{
+    "namingStrategy": {
+        "strategy": "serverNaming",
+        "dnAttribute": "uid",
+        "idAttribute": "entryUUID"
+    }
+}</programlisting>
+        </listitem>
+       </itemizedlist>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>"additionalLDAPAttributes" (optional, but necessary)</term>
+      <listitem>
+       <para>LDAP attributes to include during LDAP add operations as an
+       array of type-value lists, such as the following example.</para>
+
+       <programlisting language="javascript">{
+    "additionalLDAPAttributes": [
+        {
+            "type": "objectClass",
+            "values": [
+                "top",
+                "person",
+                "organizationalPerson",
+                "inetOrgPerson"
+            ]
+        }
+    ]
+}</programlisting>
+
+       <para>This configuration element is useful to set LDAP object classes
+       for example, which are not present in JSON resources.</para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>"etagAttribute" (optional)</term>
+      <listitem>
+       <para>The LDAP attribute to use for multi-version concurrency control
+       (MVCC).</para>
+
+      <para>Default: "etag"</para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term>"readOnUpdatePolicy" (optional)</term>
+      <listitem>
+       <para>The policy used to read an entry before it is deleted, or to
+       read an entry after it is added or modified. One of the following.</para>
+
+       <itemizedlist>
+        <listitem>
+         <para>"controls": use RFC 4527 read-entry controls to reflect the
+         state of the resource at the time the update was performed.</para>
+         <para>The directory service must support RFC 4527.</para>
+         <para>This is the default if no policy is specified.</para>
+        </listitem>
+
+        <listitem>
+         <para>"disabled": do not read the entry or return the resource on
+         update.</para>
+        </listitem>
+
+        <listitem>
+         <para>"search": perform an LDAP search to retrieve the entry before
+         deletion or after it is added or modified.</para>
+         <para>The JSON resource returned might differ from the LDAP entry that
+         was updated.</para>
+        </listitem>
+       </itemizedlist>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+
+    <para>The default mapping exposes a SCIM view of sample data.</para>
+
+    <programlisting language="javascript">{
+    "/users": {
+        "baseDN": "ou=people,dc=example,dc=com",
+        "readOnUpdatePolicy": "controls",
+        "additionalLDAPAttributes": [
+            {
+                "type": "objectClass",
+                "values": [
+                    "top",
+                    "person",
+                    "organizationalPerson",
+                    "inetOrgPerson"
+                ]
+            }
+        ],
+        "namingStrategy": {
+            "strategy": "clientDNNaming",
+            "dnAttribute": "uid"
+        },
+        "etagAttribute": "etag",
+        "attributes": {
+            "schemas": {
+                "constant": [
+                    "urn:scim:schemas:core:1.0"
+                ]
+            },
+            "id": {
+                "simple": {
+                    "ldapAttribute": "uid",
+                    "isSingleValued": true,
+                    "isRequired": true,
+                    "writability": "createOnly"
+                }
+            },
+            "rev": {
+                "simple": {
+                    "ldapAttribute": "etag",
+                    "isSingleValued": true,
+                    "writability": "readOnly"
+                }
+            },
+            "userName": {
+                "simple": {
+                    "ldapAttribute": "mail",
+                    "isSingleValued": true,
+                    "writability": "readOnly"
+                }
+            },
+            "displayName": {
+                "simple": {
+                    "ldapAttribute": "cn",
+                    "isSingleValued": true,
+                    "isRequired": true
+                }
+            },
+            "name": {
+                "object": {
+                    "givenName": {
+                        "simple": {
+                            "ldapAttribute": "givenName",
+                            "isSingleValued": true
+                        }
+                    },
+                    "familyName": {
+                        "simple": {
+                            "ldapAttribute": "sn",
+                            "isSingleValued": true,
+                            "isRequired": true
+                        }
+                    }
+                }
+            },
+            "contactInformation": {
+                "object": {
+                    "telephoneNumber": {
+                        "simple": {
+                            "ldapAttribute": "telephoneNumber",
+                            "isSingleValued": true
+                        }
+                    },
+                    "emailAddress": {
+                        "simple": {
+                            "ldapAttribute": "mail",
+                            "isSingleValued": true
+                        }
+                    }
+                }
+            }
+        }
+    }
+}</programlisting>
+   </listitem>
+  </varlistentry>
+
+  <!--
+  <varlistentry>
+   <term>"useSSL" (optional)</term>
+   <listitem>
+    <para>If the gateway connects with StartTLS or SSL to the directory
+    service, then you must specify how it uses SSL.</para>
+    <para>TODO: Not yet implemented.</para>
+   </listitem>
+  </varlistentry>
+  -->
+
+  <varlistentry>
+   <term>"secondaryLDAPServers" (optional)</term>
+   <listitem>
+    <para>The gateway accesses this array of LDAP servers if primary LDAP
+    servers cannot be contacted. These might be LDAP servers in the same
+    data center for example.</para>
+
+    <programlisting language="javascript">{
+    "secondaryLDAPServers": [
+        {
+            "hostname": "remote1.example.com",
+            "port": 1389
+        },
+        {
+            "hostname": "remote2.example.com",
+            "port": 1389
+        }
+    ]
+}</programlisting>
+
+    <para>No secondary LDAP servers are configured by default.</para>
+   </listitem>
+  </varlistentry>
+
+  <varlistentry>
+   <term>"connectionPoolSize" (optional)</term>
+   <listitem>
+    <para>The gateway creates connection pools to the primary and secondary
+    LDAP servers that maintain up to <literal>connectionPoolSize</literal>
+    connections to the servers.</para>
+
+    <para>Default: 10</para>
+
+    <programlisting language="javascript">"connectionPoolSize": 10</programlisting>
+   </listitem>
+  </varlistentry>
+
+  <varlistentry>
+   <term>"heartBeatIntervalSeconds" (optional)</term>
+   <listitem>
+    <para>The gateway tests its connections every
+    <literal>heartBeatIntervalSeconds</literal> seconds to detect whether the
+    connection is still alive.</para>
+
+    <para>Default: 30 (seconds)</para>
+
+    <programlisting language="javascript">"heartBeatIntervalSeconds": 30</programlisting>
+   </listitem>
+  </varlistentry>
+ </variablelist>
+</appendix>
diff --git a/opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-standards.xml b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-standards.xml
index 259864f..337fe4c 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-standards.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/appendix-standards.xml
@@ -20,7 +20,7 @@
   !
   ! CCPL HEADER END
   !
-  !      Copyright 2011-2012 ForgeRock AS
+  !      Copyright 2011-2013 ForgeRock AS
   !
 -->
 <appendix xml:id='appendix-standards'
@@ -984,5 +984,30 @@
     XML documents.</para>
    </listitem>
   </varlistentry>
+  <varlistentry>
+   <term><link xlink:show="new" xlink:href='http://www.json.org'
+   >JavaScript Object Notation</link></term>
+   <listitem>
+    <indexterm>
+     <primary>Supported standards</primary>
+     <secondary>JSON</secondary>
+    </indexterm>
+    <para>A data-interchange format that aims to be both "easy for humans to
+    read and write," and also "easy for machines to parse and generate."</para>
+   </listitem>
+  </varlistentry>
+  <varlistentry>
+   <term><link xlink:show="new"
+   xlink:href='http://www.simplecloud.info/specs/draft-scim-core-schema-00.html'
+   >Simple Cloud Identity Management: Core Schema 1.0</link></term>
+   <listitem>
+    <indexterm>
+     <primary>Supported standards</primary>
+     <secondary>SCIM Core Schema 1.0</secondary>
+    </indexterm>
+    <para>Platform neutral schema and extension model for representing users
+    and groups in JSON and XML formats. OpenDJ supports the JSON formats.</para>
+   </listitem>
+  </varlistentry>
  </variablelist>
 </appendix>
diff --git a/opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-listeners.xml b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-listeners.xml
index bb9881d..98dae99 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-listeners.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-listeners.xml
@@ -816,6 +816,64 @@
  --trustAll</screen>
  </section>
 
+ <section xml:id="setup-rest2ldap">
+  <title>RESTful Client Access</title>
+  <indexterm><primary>JSON</primary></indexterm>
+  <indexterm><primary>REST</primary></indexterm>
+
+  <orderedlist>
+   <para>OpenDJ offers two ways to give RESTful client applications HTTP access
+   to directory data as JSON resources.</para>
+
+   <listitem>
+    <para>Configure an additional listener on the directory server to respond
+    to REST requests.</para>
+
+    <para>With this approach, you do not need to install additional
+    software.</para>
+   </listitem>
+
+   <listitem>
+    <para>Configure the external REST LDAP gateway Servlet to access your
+    directory service.</para>
+
+    <para>With this approach, you must install the gateway separately.</para>
+   </listitem>
+  </orderedlist>
+
+  <procedure xml:id="setup-rest2ldap-connection-handler">
+   <title>To Set Up REST Access to OpenDJ Directory Server</title>
+
+   <para>Follow these steps to configure a listener for REST access directly
+   to an OpenDJ server.</para>
+
+   <step>
+    <para>TODO, https://bugster.forgerock.org/jira/browse/OPENDJ-688</para>
+   </step>
+  </procedure>
+
+  <procedure xml:id="setup-rest2ldap-gateway">
+   <title>To Set Up OpenDJ REST LDAP Gateway</title>
+
+   <para>Follow these steps to set up OpenDJ REST LDAP gateway Servlet to access
+   your directory service.</para>
+
+   <step>
+    <para>Download and install the gateway as described in <link
+    xlink:href="install-guide#install-rest2ldap-servlet"
+    xlink:role="http://docbook.org/xlink/role/olink"><citetitle>To Install
+    OpenDJ REST LDAP Gateway</citetitle></link>.</para>
+   </step>
+
+   <step>
+    <para>Adjust the configuration for your directory service as described in
+    <link xlink:href="admin-guide#appendix-rest2ldap"
+    xlink:role="http://docbook.org/xlink/role/olink"><citetitle>REST LDAP
+    Gateway Configuration</citetitle></link>.</para>
+   </step>
+  </procedure>
+ </section>
+
   <section xml:id="setup-dsml">
   <title>DSML Client Access</title>
   <indexterm><primary>DSML</primary></indexterm>
diff --git a/opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-rest-operations.xml b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-rest-operations.xml
new file mode 100644
index 0000000..ce30083
--- /dev/null
+++ b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/chap-rest-operations.xml
@@ -0,0 +1,612 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ! CCPL HEADER START
+  !
+  ! This work is licensed under the Creative Commons
+  ! Attribution-NonCommercial-NoDerivs 3.0 Unported License.
+  ! To view a copy of this license, visit
+  ! http://creativecommons.org/licenses/by-nc-nd/3.0/
+  ! or send a letter to Creative Commons, 444 Castro Street,
+  ! Suite 900, Mountain View, California, 94041, USA.
+  !
+  ! You can also obtain a copy of the license at
+  ! trunk/opendj3/legal-notices/CC-BY-NC-ND.txt.
+  ! See the License for the specific language governing permissions
+  ! and limitations under the License.
+  !
+  ! If applicable, add the following below this CCPL HEADER, with the fields
+  ! enclosed by brackets "[]" replaced with your own identifying information:
+  !      Portions Copyright [yyyy] [name of copyright owner]
+  !
+  ! CCPL HEADER END
+  !
+  !      Copyright 2013 ForgeRock AS
+  !
+-->
+<chapter xml:id='chap-rest-operations'
+         xmlns='http://docbook.org/ns/docbook' version='5.0' xml:lang='en'
+         xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
+         xsi:schemaLocation='http://docbook.org/ns/docbook http://docbook.org/xml/5.0/xsd/docbook.xsd'
+         xmlns:xlink='http://www.w3.org/1999/xlink'
+         xmlns:xinclude='http://www.w3.org/2001/XInclude'>
+ <title>Performing RESTful Operations</title>
+ <indexterm><primary>JSON</primary></indexterm>
+ <indexterm><primary>REST</primary></indexterm>
+
+ <para>OpenDJ lets you access directory data as JSON resources over HTTP. To
+ configure this capability, see <link xlink:href="admin-guide#setup-rest2ldap"
+ xlink:role="http://docbook.org/xlink/role/olink"><citetitle>REST Client
+ Access</citetitle></link> for instructions.</para>
+
+ <para>This chapter demonstrates basic RESTful client operations using the
+ default configuration and sample directory data imported into OpenDJ from
+ <link xlink:show="new" xlink:href="http://opendj.forgerock.org/Example.ldif"
+ >Example.ldif</link>.</para>
+
+ <section xml:id="understand-rest">
+  <title>Understanding the OpenDJ REST API</title>
+
+  <para>The OpenDJ REST API is built on a common ForgeRock HTTP-based REST API
+  for interacting with JSON Resources. APIs built on this common layer all let
+  you perform the following operations.</para>
+
+  <variablelist>
+   <varlistentry>
+    <term>Create</term>
+    <listitem>
+     <para>Add a resource that does not yet exist</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Read</term>
+    <listitem>
+     <para>Retrieve a single resource</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Update</term>
+    <listitem>
+     <para>Replace an existing resource</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Delete</term>
+    <listitem>
+     <para>Remove an existing resource</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Patch</term>
+    <listitem>
+     <para>Modify part of an existing resource</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Action</term>
+    <listitem>
+     <para>Perform a predefined action</para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Query</term>
+    <listitem>
+     <para>List a set of resources</para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+  <para>The present implementation in OpenDJ maps JSON resources onto LDAP
+  entries, meaning REST clients can in principle do just about anything an
+  LDAP client can do with directory data.</para>
+
+  <variablelist>
+   <para>In addition to query string parameters that depend on the operation,
+   the examples in this chapter make use of the following parameters that
+   apply to the JSON resource returned for all operations.</para>
+   <varlistentry>
+    <term><literal>_fields=<replaceable>field</replaceable>[,...]</literal></term>
+    <listitem>
+     <para>Retain only the specified fields in the JSON resource returned.</para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><literal>_prettyPrint=true|false</literal></term>
+    <listitem>
+     <para>Make the JSON resource returned easy for humans to read.</para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </section>
+
+ <section xml:id="authenticate-rest">
+  <title>Authenticating Over REST</title>
+
+  <para>TODO, https://bugster.forgerock.org/jira/browse/OPENDJ-694</para>
+ </section>
+
+ <section xml:id="create-rest">
+  <title>Creating Resources</title>
+
+  <para>There are two ways to create resources.</para>
+
+  <itemizedlist>
+   <listitem>
+    <para>To create a resource using an ID that you specify, perform an HTTP PUT
+    request with headers <literal>Content-Type: application/json</literal> and
+    <literal>If-None-Match: *</literal>, and the JSON content of your
+    resource.</para>
+
+    <para>The following example creates a new user entry with ID
+    <literal>newuser</literal>.</para>
+
+    <screen>$ curl
+ --request PUT
+ --header "Content-Type: application/json"
+ --header "If-None-Match: *"
+ --data '{
+  "id": "newuser",
+  "contactInformation": {
+    "telephoneNumber": "+1 408 555 1212",
+    "emailAddress": "newuser@example.com"
+  },
+  "name": {
+    "familyName": "New",
+    "givenName": "User"
+  },
+  "displayName": "New User"
+ }'
+ http://opendj.example.com:8080/rest2ldap/users/newuser?_prettyPrint=true
+{
+  "id" : "newuser",
+  "rev" : "0000000049522179",
+  "schemas" : [ "urn:scim:schemas:core:1.0" ],
+  "contactInformation" : {
+    "telephoneNumber" : "+1 408 555 1212",
+    "emailAddress" : "newuser@example.com"
+  },
+  "name" : {
+    "familyName" : "New",
+    "givenName" : "User"
+  },
+  "userName" : "newuser@example.com",
+  "displayName" : "New User"
+}</screen>
+   </listitem>
+
+   <listitem>
+    <para>To create a resource letting the server choose the ID, perform an HTTP
+    POST with <literal>_action=create</literal> as described in
+    <xref linkend="action-rest" />.</para>
+   </listitem>
+  </itemizedlist>
+ </section>
+
+ <section xml:id="read-rest">
+  <title>Reading a Resource</title>
+
+  <para>To read a resource, perform an HTTP GET.</para>
+
+  <screen>$ curl http://opendj.example.com:8080/rest2ldap/users/bjensen?_prettyPrint=true
+{
+  "id" : "bjensen",
+  "rev" : "000000002f43b789",
+  "schemas" : [ "urn:scim:schemas:core:1.0" ],
+  "contactInformation" : {
+    "telephoneNumber" : "+1 408 555 1862",
+    "emailAddress" : "bjensen@example.com"
+  },
+  "name" : {
+    "familyName" : "Jensen",
+    "givenName" : "Barbara"
+  },
+  "userName" : "bjensen@example.com",
+  "displayName" : "Barbara Jensen"
+}</screen>
+ </section>
+
+ <section xml:id="update-rest">
+  <title>Updating Resources</title>
+
+  <para>TODO, https://bugster.forgerock.org/jira/browse/OPENDJ-693</para>
+ </section>
+
+ <section xml:id="delete-rest">
+  <title>Deleting Resources</title>
+
+  <para>TODO, https://bugster.forgerock.org/jira/browse/OPENDJ-692</para>
+ </section>
+
+ <section xml:id="patch-rest">
+  <title>Patching Resources</title>
+
+  <para>TODO, https://bugster.forgerock.org/jira/browse/CREST-3</para>
+ </section>
+
+ <section xml:id="action-rest">
+  <title>Using Actions</title>
+
+  <para>OpenDJ implements an action that lets the server choose the resource ID
+  on creation. To use this action, perform an HTTP POST with header
+  <literal>Content-Type: application/json</literal>,
+  <literal>_action=create</literal> in the query string, and the JSON content of
+  your resource.</para>
+
+  <para>The following example creates a new user entry. Lines are folded for
+  readability.</para>
+
+  <para>TODO, fix pending https://bugster.forgerock.org/jira/browse/OPENDJ-775</para>
+  <screen>$ curl
+ --request POST
+ --header "Content-Type: application/json"
+ --data '{
+  "id": "newuser",
+  "contactInformation": {
+    "telephoneNumber": "+1 408 555 1212",
+    "emailAddress": "newuser@example.com"
+  },
+  "name": {
+    "familyName": "New",
+    "givenName": "User"
+  },
+  "displayName": "New User"
+}'
+ http://opendj.example.com:8080/rest2ldap/users?_action=create&amp;_prettyPrint=true
+{
+  "id": "newuser",
+  "rev": "0000000049522179",
+  "schemas": [
+    "urn:scim:schemas:core:1.0"
+  ],
+  "contactInformation": {
+    "telephoneNumber": "+1 408 555 1212",
+    "emailAddress": "newuser@example.com"
+  },
+  "name": {
+    "familyName": "New",
+    "givenName": "User"
+  },
+  "userName": "newuser@example.com",
+  "displayName": "New User"
+}</screen>
+
+  <para>TODO, https://bugster.forgerock.org/jira/browse/OPENDJ-695</para>
+ </section>
+
+ <section xml:id="query-rest">
+  <title>Querying Resource Collections</title>
+
+  <para>To query resource collections, perform an HTTP GET with a
+  <literal>_filter=<replaceable>filter</replaceable></literal> parameter in
+  your query string.</para>
+
+  <variablelist>
+   <para>For query operations, your <replaceable>filter</replaceable>
+   expressions are constructed from the following building blocks.
+   Make sure you URL encode the filter expressions, which are shown here
+   without URL encoding to make them easier to read.</para>
+
+   <para>In these expressions the simplest
+   <replaceable>json-pointer</replaceable> is a field of the JSON resource,
+   such as <literal>userName</literal> or <literal>id</literal>. A
+   <replaceable>json-pointer</replaceable> can however point to nested
+   elements as described in the <link xlink:show="new"
+   xlink:href="http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer">JSON
+   Pointer</link> Internet-Draft.</para>
+
+   <varlistentry>
+    <term>Comparison expressions</term>
+    <listitem>
+     <para>You can build filters using the following comparison expressions.</para>
+
+     <para>Request URLs are folded in the following examples to make them
+     easier to read.</para>
+
+     <variablelist>
+      <varlistentry>
+       <term><literal><replaceable>json-pointer</replaceable> eq <replaceable>json-value</replaceable></literal></term>
+       <listitem>
+        <para>Matches when the pointer equals the value, as in the following
+        example.</para>
+
+        <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users?
+  _filter=userName+eq+"bjensen@example.com"
+  &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "id" : "bjensen",
+    "rev" : "000000002f43b789",
+    "schemas" : [ "urn:scim:schemas:core:1.0" ],
+    "contactInformation" : {
+      "telephoneNumber" : "+1 408 555 1862",
+      "emailAddress" : "bjensen@example.com"
+    },
+    "name" : {
+      "familyName" : "Jensen",
+      "givenName" : "Barbara"
+    },
+    "userName" : "bjensen@example.com",
+    "displayName" : "Barbara Jensen"
+  } ],
+  "resultCount" : 1,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><literal><replaceable>json-pointer</replaceable> co <replaceable>json-value</replaceable></literal></term>
+       <listitem>
+        <para>Matches when the pointer contains the value, as in the following
+        example.</para>
+
+        <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users?
+ _filter=userName+co+"jensen"
+ &amp;_fields=userName
+ &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "userName" : "ajensen@example.com"
+  }, {
+    "userName" : "bjensen@example.com"
+  }, {
+    "userName" : "gjensen@example.com"
+  }, {
+    "userName" : "jjensen@example.com"
+  }, {
+    "userName" : "kjensen@example.com"
+  }, {
+    "userName" : "rjensen@example.com"
+  }, {
+    "userName" : "tjensen@example.com"
+  } ],
+  "resultCount" : 7,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><literal><replaceable>json-pointer</replaceable> sw <replaceable>json-value</replaceable></literal></term>
+       <listitem>
+        <para>Matches when the pointer starts with the value, as in the
+        following example.</para>
+
+        <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users?
+ _filter=userName+sw+"ab"
+ &amp;_fields=userName
+ &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "userName" : "abarnes@example.com"
+  }, {
+    "userName" : "abergin@example.com"
+  } ],
+  "resultCount" : 2,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><literal><replaceable>json-pointer</replaceable> lt <replaceable>json-value</replaceable></literal></term>
+       <listitem>
+        <para>Matches when the pointer is less than the value, as in the
+        following example.</para>
+
+        <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users?
+ _filter=userName+lt+"ac"
+ &amp;_fields=userName
+ &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "userName" : "abarnes@example.com"
+  }, {
+    "userName" : "abergin@example.com"
+  } ],
+  "resultCount" : 2,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><literal><replaceable>json-pointer</replaceable> le <replaceable>json-value</replaceable></literal></term>
+       <listitem>
+        <para>Matches when the pointer is less than or equal to the value, as
+        in the following example.</para>
+
+        <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users?
+ _filter=userName+le+"ad"
+ &amp;_fields=userName
+ &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "userName" : "abarnes@example.com"
+  }, {
+    "userName" : "abergin@example.com"
+  }, {
+    "userName" : "achassin@example.com"
+  } ],
+  "resultCount" : 3,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><literal><replaceable>json-pointer</replaceable> gt <replaceable>json-value</replaceable></literal></term>
+       <listitem>
+        <para>Matches when the pointer is greater than the value, as in the
+        following example.</para>
+
+        <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users?
+ _filter=userName+gt+"tt"
+ &amp;_fields=userName
+ &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "userName" : "ttully@example.com"
+  }, {
+    "userName" : "tward@example.com"
+  }, {
+    "userName" : "wlutz@example.com"
+  } ],
+  "resultCount" : 3,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term><literal><replaceable>json-pointer</replaceable> ge <replaceable>json-value</replaceable></literal></term>
+       <listitem>
+        <para>Matches when the pointer is greater than or equal to the value,
+        as in the following example.</para>
+
+        <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users?
+ _filter=userName+ge+"tw"
+ &amp;_fields=userName
+ &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "userName" : "tward@example.com"
+  }, {
+    "userName" : "wlutz@example.com"
+  } ],
+  "resultCount" : 2,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+       </listitem>
+      </varlistentry>
+
+      <!--
+      <varlistentry>
+       <term><literal><replaceable>json-pointer</replaceable> <replaceable>string</replaceable> <replaceable>json-value</replaceable></literal></term>
+       <listitem>
+        <para>Matches an extended comparison.</para>
+        <screen>TODO</screen>
+       </listitem>
+      </varlistentry>
+      -->
+     </variablelist>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Presence expression</term>
+    <listitem>
+     <para><literal><replaceable>json-pointer</replaceable> pr</literal> matches
+     any resource on which the <replaceable>json-pointer</replaceable> is
+     present, as in the following example.</para>
+
+     <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users?_filter=userName%20pr
+ &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "id" : "abarnes",
+    "rev" : "000000002609a565",
+    "schemas" : [ "urn:scim:schemas:core:1.0" ],
+    "contactInformation" : {
+... many entries omitted ...
+    "userName" : "fdupont@example.fr",
+    "displayName" : "Frederique Dupont"
+  } ],
+  "resultCount" : 151,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Literal expressions</term>
+    <listitem>
+     <para><literal>true</literal> matches any resource in the collection.</para>
+     <para><literal>false</literal> matches no resource in the collection.</para>
+
+     <para>In other words you can list all resources in a collection as in the
+     following example.</para>
+
+     <screen>$ curl http://opendj.example.com:8080/rest2ldap/users?_filter=true
+... much output omitted ...</screen>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term>Complex expressions</term>
+    <listitem>
+     <para>You can combine expressions using boolean operators
+     <literal>and</literal>, <literal>or</literal>, and <literal>!</literal>
+     (not), using parentheses,
+     <literal>(<replaceable>expression</replaceable>)</literal>, to group
+     expressions. The following example queries resources with last name
+     Jensen and first name starting with <literal>Bar</literal>. Notice that the
+     filters use the JSON pointers <literal>name/familyName</literal> and
+     <literal>name/givenName</literal> to identify the fields that are nested
+     inside the <literal>name</literal> object.</para>
+
+     <screen>$ curl 'http://opendj.example.com:8080/rest2ldap/users
+ ?_filter=(name/familyName+eq+"jensen"+and+name/givenName+sw+"Bar")
+ &amp;_fields=name
+ &amp;_prettyPrint=true'
+{
+  "result" : [ {
+    "name" : {
+      "familyName" : "Jensen",
+      "givenName" : "Barbara"
+    }
+  } ],
+  "resultCount" : 1,
+  "pagedResultsCookie" : null,
+  "remainingPagedResults" : -1
+}</screen>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+
+  <para>You can have the server sort JSON resources before it returns them by
+  using the <literal>_sortKeys[+-]=<replaceable>field</replaceable>[,...]</literal>
+  query string. TODO, pending implementation https://bugster.forgerock.org/jira/browse/OPENDJ-702</para>
+
+  <variablelist>
+   <para>You can page through search results using the following query string
+   parameters.</para>
+
+   <para>TODO, pending implementation https://bugster.forgerock.org/jira/browse/OPENDJ-701</para>
+
+   <varlistentry>
+    <term><literal>__pagedResultsCookie=<replaceable>string</replaceable></literal></term>
+    <listitem>
+     <para></para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>__pagedResultsOffset=<replaceable>string</replaceable></literal></term>
+    <listitem>
+     <para></para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>__pagedResultsCookie=<replaceable>string</replaceable></literal></term>
+    <listitem>
+     <para></para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </section>
+</chapter>
diff --git a/opendj-sdk/opendj3/src/main/docbkx/admin-guide/index.xml b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/index.xml
index fed89e1..da38cec 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/admin-guide/index.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/admin-guide/index.xml
@@ -65,6 +65,7 @@
  <xinclude:include href='chap-listeners.xml' />
  <xinclude:include href='chap-privileges-acis.xml' />
  <xinclude:include href='chap-ldap-operations.xml' />
+ <xinclude:include href='chap-rest-operations.xml' />
  <xinclude:include href='chap-indexing.xml' />
  <xinclude:include href='chap-replication.xml' />
  <xinclude:include href='chap-backup-restore.xml' />
@@ -134,6 +135,7 @@
 
  <xinclude:include href="../shared/glossary.xml" />
 
+ <xinclude:include href="appendix-rest2ldap.xml" />
  <xinclude:include href='appendix-file-layout.xml' />
  <xinclude:include href='appendix-ports-used.xml' />
  <xinclude:include href='appendix-standards.xml' />
diff --git a/opendj-sdk/opendj3/src/main/docbkx/install-guide/chap-install-cli.xml b/opendj-sdk/opendj3/src/main/docbkx/install-guide/chap-install-cli.xml
index 0983653..d1e8e5a 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/install-guide/chap-install-cli.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/install-guide/chap-install-cli.xml
@@ -465,6 +465,88 @@
   </step>
  </procedure>
 
+ <procedure xml:id="install-rest2ldap-servlet">
+  <title>To Install OpenDJ REST LDAP Gateway</title>
+  <indexterm><primary>REST LDAP gateway</primary></indexterm>
+
+  <para>The OpenDJ REST LDAP gateway functions as a web application in a web
+  application container, running independently of OpenDJ. <!-- Alternatively,
+  you can add an HTTP listener directly to OpenDJ. See <link
+  xlink:href="admin-guide#setup-rest2ldap"
+  xlink:role="http://docbook.org/xlink/role/olink"><citetitle>REST Client
+  Access</citetitle></link> for instructions.--> You configure the gateway
+  to access your directory service by editing
+  <filename>opendj-rest2ldap-servlet.json</filename> where you deploy the
+  gateway web application.</para>
+
+  <step>
+   <para>Deploy
+   <filename>opendj-rest2ldap-servlet-<?eval ${docTargetVersion}?>-servlet.war</filename>
+   according to the instructions for your application server.</para>
+  </step>
+
+  <step>
+   <para>Edit <filename>opendj-rest2ldap-servlet.json</filename> where you
+   deployed the gateway web application.</para>
+
+   <para>The default JSON resource for the configuration includes both
+   connection and authentication information, and also
+   <literal>mappings</literal>. The <literal>mappings</literal> describe how
+   the gateway translates between JSON and LDAP representations of your
+   data. The default <literal>mappings</literal> are built to work with
+   generated example data and also the sample content in <link xlink:show="new"
+   xlink:href="http://opendj.forgerock.org/Example.ldif"
+    >Example.ldif</link>.</para>
+
+   <para>At minimum, make sure that the host name and port numbers for
+   <literal>primaryLDAPServers</literal> are properly configured, that
+   <literal>authentication</literal> reflects the correct simple bind
+   credentials, and that the <literal>mappings</literal> for the endpoints
+   correctly match your directory data.</para>
+
+   <para>For details on the configuration, see <link
+   xlink:href="admin-guide#appendix-rest2ldap"
+   xlink:role="http://docbook.org/xlink/role/olink"><citetitle>REST LDAP
+   Gateway Configuration</citetitle></link>.</para>
+  </step>
+
+  <step>
+   <para>Restart the REST LDAP gateway or the application server to make
+   sure the changes are taken into account.</para>
+  </step>
+
+  <step>
+   <para>Make sure that your directory server is running, and then check that
+   the gateway is connecting correctly.</para>
+
+   <para>The following command reads Babs Jensen's entry through the gateway
+   to the backend holding data from <filename>Example.ldif</filename>.</para>
+
+   <screen>$ curl
+ http://opendj.example.com:8080/rest2ldap/users/bjensen?_prettyPrint=true
+{
+  "id" : "bjensen",
+  "rev" : "000000002f43b789",
+  "schemas" : [ "urn:scim:schemas:core:1.0" ],
+  "contactInformation" : {
+    "telephoneNumber" : "+1 408 555 1862",
+    "emailAddress" : "bjensen@example.com"
+  },
+  "name" : {
+    "familyName" : "Jensen",
+    "givenName" : "Barbara"
+  },
+  "userName" : "bjensen@example.com",
+  "displayName" : "Barbara Jensen"
+}</screen>
+
+   <para>If you generated example data, Babs Jensen's entry is not included.
+   Try a URL such as
+   <literal>http://opendj.example.com:8080/rest2ldap/users/user.0</literal>
+   instead.</para>
+  </step>
+ </procedure>
+
  <procedure xml:id="install-dsml-gateway">
   <title>To Install OpenDJ DSML gateway</title>
   <indexterm><primary>DSML gateway</primary></indexterm>
diff --git a/opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-before-you-install.xml b/opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-before-you-install.xml
index 95af958..aaaf08d 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-before-you-install.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-before-you-install.xml
@@ -133,8 +133,8 @@
   <para>OpenDJ directory server runs as a standalone Java service, and
   does not depend on an application server.</para>
 
-  <para>OpenDJ <?eval ${docTargetVersion}?> DSML gateway has been validated
-  on Apache Tomcat 6.</para>
+  <para>OpenDJ <?eval ${docTargetVersion}?> DSML and REST LDAP gateway Servlets
+  have been validated on Apache Tomcat 6.</para>
  </section>
 
  <section xml:id="prerequisites-fqdn">
diff --git a/opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-whats-new.xml b/opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-whats-new.xml
index ee7b2af..bfbf726 100644
--- a/opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-whats-new.xml
+++ b/opendj-sdk/opendj3/src/main/docbkx/release-notes/chap-whats-new.xml
@@ -68,6 +68,14 @@
      <para>TODO: Bring this list up to date post OpenDJ 2.5.0-Xpress1.</para>
     </listitem>
     <listitem>
+     <para>OpenDJ now provides RESTful access to directory data (<link
+     xlink:show="new"
+     xlink:href="https://bugster.forgerock.org/jira/browse/OPENDJ-687"
+     >OPENDJ-687</link>, <link xlink:show="new"
+     xlink:href="https://bugster.forgerock.org/jira/browse/OPENDJ-688"
+     >OPENDJ-688</link>).</para>
+    </listitem>
+    <listitem>
      <para>OpenDJ now sets <literal>isMemberOf</literal> on groups as well as
      user entries (<link xlink:show="new"
      xlink:href="https://bugster.forgerock.org/jira/browse/OPENDJ-513"

--
Gitblit v1.10.0