001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2010 Sun Microsystems, Inc.
015 * Portions Copyright 2016 ForgeRock AS.
016 */
017package org.forgerock.opendj.ldap.controls;
018
019import static com.forgerock.opendj.ldap.CoreMessages.ERR_SUBTREE_DELETE_CONTROL_BAD_OID;
020import static com.forgerock.opendj.ldap.CoreMessages.ERR_SUBTREE_DELETE_INVALID_CONTROL_VALUE;
021
022import org.forgerock.i18n.LocalizableMessage;
023import org.forgerock.opendj.ldap.ByteString;
024import org.forgerock.opendj.ldap.DecodeException;
025import org.forgerock.opendj.ldap.DecodeOptions;
026
027import org.forgerock.util.Reject;
028
029/**
030 * The tree delete request control as defined in draft-armijo-ldap-treedelete.
031 * This control allows a client to delete an entire subtree of a container entry
032 * in a single delete operation.
033 *
034 * <pre>
035 * Connection connection = ...;
036 * String baseDN = ...;
037 *
038 * DeleteRequest request =
039 *         Requests.newDeleteRequest(baseDN)
040 *             .addControl(SubtreeDeleteRequestControl.newControl(true));
041 * connection.delete(request);
042 * </pre>
043 *
044 * @see <a
045 *      href="http://tools.ietf.org/html/draft-armijo-ldap-treedelete">draft-armijo-ldap-treedelete
046 *      - Tree Delete Control </a>
047 */
048public final class SubtreeDeleteRequestControl implements Control {
049    /** The OID for the subtree delete request control. */
050    public static final String OID = "1.2.840.113556.1.4.805";
051
052    private static final SubtreeDeleteRequestControl CRITICAL_INSTANCE =
053            new SubtreeDeleteRequestControl(true);
054
055    private static final SubtreeDeleteRequestControl NONCRITICAL_INSTANCE =
056            new SubtreeDeleteRequestControl(false);
057
058    /** A decoder which can be used for decoding the sub-tree delete request control. */
059    public static final ControlDecoder<SubtreeDeleteRequestControl> DECODER =
060            new ControlDecoder<SubtreeDeleteRequestControl>() {
061
062                @Override
063                public SubtreeDeleteRequestControl decodeControl(final Control control,
064                        final DecodeOptions options) throws DecodeException {
065                    Reject.ifNull(control);
066
067                    if (control instanceof SubtreeDeleteRequestControl) {
068                        return (SubtreeDeleteRequestControl) control;
069                    }
070
071                    if (!control.getOID().equals(OID)) {
072                        final LocalizableMessage message =
073                                ERR_SUBTREE_DELETE_CONTROL_BAD_OID.get(control.getOID(), OID);
074                        throw DecodeException.error(message);
075                    }
076
077                    if (control.hasValue()) {
078                        final LocalizableMessage message =
079                                ERR_SUBTREE_DELETE_INVALID_CONTROL_VALUE.get();
080                        throw DecodeException.error(message);
081                    }
082
083                    return control.isCritical() ? CRITICAL_INSTANCE : NONCRITICAL_INSTANCE;
084                }
085
086                @Override
087                public String getOID() {
088                    return OID;
089                }
090            };
091
092    /**
093     * Creates a new tree delete request control having the provided
094     * criticality.
095     *
096     * @param isCritical
097     *            {@code true} if it is unacceptable to perform the operation
098     *            without applying the semantics of this control, or
099     *            {@code false} if it can be ignored.
100     * @return The new control.
101     */
102    public static SubtreeDeleteRequestControl newControl(final boolean isCritical) {
103        return isCritical ? CRITICAL_INSTANCE : NONCRITICAL_INSTANCE;
104    }
105
106    private final boolean isCritical;
107
108    private SubtreeDeleteRequestControl(final boolean isCritical) {
109        this.isCritical = isCritical;
110    }
111
112    @Override
113    public String getOID() {
114        return OID;
115    }
116
117    @Override
118    public ByteString getValue() {
119        return null;
120    }
121
122    @Override
123    public boolean hasValue() {
124        return false;
125    }
126
127    @Override
128    public boolean isCritical() {
129        return isCritical;
130    }
131
132    @Override
133    public String toString() {
134        final StringBuilder builder = new StringBuilder();
135        builder.append("SubtreeDeleteRequestControl(oid=");
136        builder.append(getOID());
137        builder.append(", criticality=");
138        builder.append(isCritical());
139        builder.append(")");
140        return builder.toString();
141    }
142
143}