001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *      Copyright 2013 ForgeRock AS
024 */
025package org.forgerock.opendj.ldap.controls;
026
027import org.forgerock.opendj.ldap.ByteString;
028
029/**
030 * The persistent search request control for Active Directory as defined by
031 * Microsoft. This control allows a client to receive notification of changes
032 * that occur in an Active Directory server.
033 * <br/>
034 *
035 * <pre>
036 * Connection connection = ...;
037 *
038 * SearchRequest request =
039 *         Requests.newSearchRequest("dc=example,dc=com",
040 *                 SearchScope.WHOLE_SUBTREE, "(objectclass=*)", "cn",
041 *                 "isDeleted", "whenChanged", "whenCreated").addControl(
042 *                 ADNotificationRequestControl.newControl(true));
043 *
044 * ConnectionEntryReader reader = connection.search(request);
045 *
046 * while (reader.hasNext()) {
047 *     if (!reader.isReference()) {
048 *         SearchResultEntry entry = reader.readEntry(); // Entry that changed
049 *
050 *         Boolean isDeleted = entry.parseAttribute("isDeleted").asBoolean();
051 *         if (isDeleted != null && isDeleted) {
052 *             // Handle entry deletion
053 *         }
054 *         String whenCreated = entry.parseAttribute("whenCreated").asString();
055 *         String whenChanged = entry.parseAttribute("whenChanged").asString();
056 *         if (whenCreated != null && whenChanged != null) {
057 *             if (whenCreated.equals(whenChanged)) {
058 *                 //Handle entry addition
059 *             } else {
060 *                 //Handle entry modification
061 *             }
062 *         }
063 *     } else {
064 *         reader.readReference(); //read and ignore reference
065 *     }
066 * }
067 *
068 * </pre>
069 *
070 * @see <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa772153(v=vs.85).aspx">
071 *      Change Notifications in Active Directory Domain Services</a>
072 */
073public final class ADNotificationRequestControl implements Control {
074
075    /**
076     * The OID for the Microsoft Active Directory persistent search request
077     * control. The control itself is empty and the changes are returned as
078     * attributes, such as "isDeleted", "whenChanged", "whenCreated".
079     */
080    public static final String OID = "1.2.840.113556.1.4.528";
081
082    /**
083     * The name of the isDeleted attribute as defined in the Active Directory
084     * schema. If the value of the attribute is <code>TRUE</code>, the object
085     * has been marked for deletion.
086     */
087    public static final String IS_DELETED_ATTR = "isDeleted";
088
089    /**
090     * The name of the whenCreated attribute as defined in the Active Directory
091     * schema. Holds the date of the creation of the object in GeneralizedTime
092     * format.
093     */
094    public static final String WHEN_CREATED_ATTR = "whenCreated";
095
096    /**
097     * The name of the whenChanged attribute as defined in the Active Directory
098     * schema. Holds the date of the last modification of the object in
099     * GeneralizedTime format.
100     */
101    public static final String WHEN_CHANGED_ATTR = "whenChanged";
102
103    /**
104     * The name of the objectGUID attribute as defined in the Active Directory
105     * schema. This is the unique identifier of an object stored in binary
106     * format.
107     */
108    public static final String OBJECT_GUID_ATTR = "objectGUID";
109
110    /**
111     * The name of the uSNChanged attribute as defined in the Active Directory
112     * schema. This attribute can be used to determine whether the current
113     * state of the object on the server reflects the latest changes that the
114     * client has received.
115     */
116    public static final String USN_CHANGED_ATTR = "uSNChanged";
117
118    private final boolean isCritical;
119
120    private ADNotificationRequestControl(final boolean isCritical) {
121        this.isCritical = isCritical;
122    }
123
124    /**
125     * Creates a new Active Directory change notification request control.
126     *
127     * @param isCritical
128     *            {@code true} if it is unacceptable to perform the operation
129     *            without applying the semantics of this control, or
130     *            {@code false} if it can be ignored
131     * @return The new control.
132     */
133    public static ADNotificationRequestControl newControl(final boolean isCritical) {
134        return new ADNotificationRequestControl(isCritical);
135    }
136
137    /** {@inheritDoc} */
138    @Override
139    public String getOID() {
140        return OID;
141    }
142
143    /** {@inheritDoc} */
144    @Override
145    public ByteString getValue() {
146        return null;
147    }
148
149    /** {@inheritDoc} */
150    @Override
151    public boolean hasValue() {
152        return false;
153    }
154
155    /** {@inheritDoc} */
156    @Override
157    public boolean isCritical() {
158        return isCritical;
159    }
160
161    /** {@inheritDoc} */
162    @Override
163    public String toString() {
164        final StringBuilder builder = new StringBuilder();
165        builder.append("ADNotificationRequestControl(oid=");
166        builder.append(getOID());
167        builder.append(", criticality=");
168        builder.append(isCritical());
169        builder.append(")");
170        return builder.toString();
171    }
172}