001/* 002 * Copyright 2007-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.controls; 022 023 024 025import com.unboundid.asn1.ASN1Element; 026import com.unboundid.asn1.ASN1OctetString; 027import com.unboundid.asn1.ASN1Sequence; 028import com.unboundid.ldap.sdk.Control; 029import com.unboundid.ldap.sdk.LDAPException; 030import com.unboundid.ldap.sdk.ResultCode; 031import com.unboundid.util.NotMutable; 032import com.unboundid.util.StaticUtils; 033import com.unboundid.util.ThreadSafety; 034import com.unboundid.util.ThreadSafetyLevel; 035 036import static com.unboundid.ldap.sdk.controls.ControlMessages.*; 037import static com.unboundid.util.Debug.*; 038 039 040 041/** 042 * This class provides an implementation of the LDAP post-read request control 043 * as defined in <A HREF="http://www.ietf.org/rfc/rfc4527.txt">RFC 4527</A>. It 044 * may be used to request that the server retrieve a copy of the target entry as 045 * it appeared immediately after processing an add, modify, or modify DN 046 * operation. 047 * <BR><BR> 048 * If this control is included in an add, modify, or modify DN request, then the 049 * corresponding response may include a {@link PostReadResponseControl} 050 * containing a version of the entry as it appeared after applying that change. 051 * Note that this response control will only be included if the operation was 052 * successful, so it will not be provided if the operation failed for some 053 * reason (e.g., if the change would have violated the server schema, or if the 054 * requester did not have sufficient permission to perform that operation). 055 * <BR><BR> 056 * The value of this control should contain a set of requested attributes to 057 * include in the entry that is returned. The server should treat this set of 058 * requested attributes exactly as it treats the requested attributes from a 059 * {@link com.unboundid.ldap.sdk.SearchRequest}. As is the case with a search 060 * request, if no attributes are specified, then all user attributes will be 061 * included. 062 * <BR><BR> 063 * <H2>Example</H2> 064 * The following example demonstrates the use of the pre-read and post-read 065 * controls. It will modify an entry to increment the value of the 066 * {@code test-counter} attribute by one, and will use the pre-read and 067 * post-read controls to determine what the previous and updated values are: 068 * <PRE> 069 * // Create a modify request that we can use to increment the value of a 070 * // custom attribute named "test-counter". 071 * ModifyRequest modifyRequest = new ModifyRequest( 072 * "uid=test.user,ou=People,dc=example,dc=com", 073 * new Modification(ModificationType.INCREMENT, 074 * "test-counter", // The attribute to increment. 075 * "1")); // The amount by which to increment the value. 076 * 077 * // Update the modify request to add both pre-read and post-read request 078 * // controls to see what the entry value was before and after the change. 079 * // We only care about getting the test-counter attribute. 080 * modifyRequest.setControls( 081 * new PreReadRequestControl("test-counter"), 082 * new PostReadRequestControl("test-counter")); 083 * 084 * // Process the modify operation in the server. 085 * LDAPResult modifyResult; 086 * try 087 * { 088 * modifyResult = connection.modify(modifyRequest); 089 * // If we got here, then the modification should have been successful. 090 * } 091 * catch (LDAPException le) 092 * { 093 * // This indicates that the operation did not complete successfully. 094 * modifyResult = le.toLDAPResult(); 095 * ResultCode resultCode = le.getResultCode(); 096 * String errorMessageFromServer = le.getDiagnosticMessage(); 097 * } 098 * LDAPTestUtils.assertResultCodeEquals(modifyResult, ResultCode.SUCCESS); 099 * 100 * // Get the pre-read and post-read response controls from the server and 101 * // retrieve the before and after values for the test-counter attribute. 102 * LDAPTestUtils.assertHasControl(modifyResult, 103 * PreReadResponseControl.PRE_READ_RESPONSE_OID); 104 * PreReadResponseControl preReadResponse = 105 * PreReadResponseControl.get(modifyResult); 106 * Integer beforeValue = 107 * preReadResponse.getEntry().getAttributeValueAsInteger("test-counter"); 108 * 109 * LDAPTestUtils.assertHasControl(modifyResult, 110 * PostReadResponseControl.POST_READ_RESPONSE_OID); 111 * PostReadResponseControl postReadResponse = 112 * PostReadResponseControl.get(modifyResult); 113 * Integer afterValue = 114 * postReadResponse.getEntry().getAttributeValueAsInteger("test-counter"); 115 * </PRE> 116 */ 117@NotMutable() 118@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 119public final class PostReadRequestControl 120 extends Control 121{ 122 /** 123 * The OID (1.3.6.1.1.13.2) for the post-read request control. 124 */ 125 public static final String POST_READ_REQUEST_OID = "1.3.6.1.1.13.2"; 126 127 128 129 /** 130 * The set of requested attributes that will be used if none are provided. 131 */ 132 private static final String[] NO_ATTRIBUTES = StaticUtils.NO_STRINGS; 133 134 135 136 /** 137 * The serial version UID for this serializable class. 138 */ 139 private static final long serialVersionUID = -4210061989410209462L; 140 141 142 143 // The set of requested attributes to retrieve from the target entry. 144 private final String[] attributes; 145 146 147 148 /** 149 * Creates a new post-read request control that will retrieve the specified 150 * set of attributes from the target entry. It will be marked critical. 151 * 152 * @param attributes The set of attributes to retrieve from the target 153 * entry. It behaves in the same way as the set of 154 * requested attributes for a search operation. If this 155 * is empty or {@code null}, then all user attributes 156 * will be returned. 157 */ 158 public PostReadRequestControl(final String... attributes) 159 { 160 this(true, attributes); 161 } 162 163 164 165 /** 166 * Creates a new post-read request control that will retrieve the specified 167 * set of attributes from the target entry. 168 * 169 * @param isCritical Indicates whether this control should be marked 170 * critical. 171 * @param attributes The set of attributes to retrieve from the target 172 * entry. It behaves in the same way as the set of 173 * requested attributes for a search operation. If this 174 * is empty or {@code null}, then all user attributes 175 * will be returned. 176 */ 177 public PostReadRequestControl(final boolean isCritical, 178 final String... attributes) 179 { 180 super(POST_READ_REQUEST_OID, isCritical, encodeValue(attributes)); 181 182 if (attributes == null) 183 { 184 this.attributes = NO_ATTRIBUTES; 185 } 186 else 187 { 188 this.attributes = attributes; 189 } 190 } 191 192 193 194 /** 195 * Creates a new post-read request control which is decoded from the provided 196 * generic control. 197 * 198 * @param control The generic control to be decoded as a post-read request 199 * control. 200 * 201 * @throws LDAPException If the provided control cannot be decoded as a 202 * post-read request control. 203 */ 204 public PostReadRequestControl(final Control control) 205 throws LDAPException 206 { 207 super(control); 208 209 final ASN1OctetString value = control.getValue(); 210 if (value == null) 211 { 212 throw new LDAPException(ResultCode.DECODING_ERROR, 213 ERR_POST_READ_REQUEST_NO_VALUE.get()); 214 } 215 216 try 217 { 218 final ASN1Element valueElement = ASN1Element.decode(value.getValue()); 219 final ASN1Element[] attrElements = 220 ASN1Sequence.decodeAsSequence(valueElement).elements(); 221 attributes = new String[attrElements.length]; 222 for (int i=0; i < attrElements.length; i++) 223 { 224 attributes[i] = 225 ASN1OctetString.decodeAsOctetString(attrElements[i]).stringValue(); 226 } 227 } 228 catch (Exception e) 229 { 230 debugException(e); 231 throw new LDAPException(ResultCode.DECODING_ERROR, 232 ERR_POST_READ_REQUEST_CANNOT_DECODE.get(e), e); 233 } 234 } 235 236 237 238 /** 239 * Encodes the provided information into an octet string that can be used as 240 * the value for this control. 241 * 242 * @param attributes The set of attributes to retrieve from the target 243 * entry. It behaves in the same way as the set of 244 * requested attributes for a search operation. If this 245 * is empty or {@code null}, then all user attributes 246 * will be returned. 247 * 248 * @return An ASN.1 octet string that can be used as the value for this 249 * control. 250 */ 251 private static ASN1OctetString encodeValue(final String[] attributes) 252 { 253 if ((attributes == null) || (attributes.length == 0)) 254 { 255 return new ASN1OctetString(new ASN1Sequence().encode()); 256 } 257 258 final ASN1OctetString[] elements = new ASN1OctetString[attributes.length]; 259 for (int i=0; i < attributes.length; i++) 260 { 261 elements[i] = new ASN1OctetString(attributes[i]); 262 } 263 264 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 265 } 266 267 268 269 /** 270 * Retrieves the set of attributes that will be requested for inclusion in the 271 * entry returned in the response control. 272 * 273 * @return The set of attributes that will be requested for inclusion in the 274 * entry returned in the response control, or an empty array if all 275 * user attributes should be returned. 276 */ 277 public String[] getAttributes() 278 { 279 return attributes; 280 } 281 282 283 284 /** 285 * {@inheritDoc} 286 */ 287 @Override() 288 public String getControlName() 289 { 290 return INFO_CONTROL_NAME_POST_READ_REQUEST.get(); 291 } 292 293 294 295 /** 296 * {@inheritDoc} 297 */ 298 @Override() 299 public void toString(final StringBuilder buffer) 300 { 301 buffer.append("PostReadRequestControl(attributes={"); 302 for (int i=0; i < attributes.length; i++) 303 { 304 if (i > 0) 305 { 306 buffer.append(", "); 307 } 308 buffer.append('\''); 309 buffer.append(attributes[i]); 310 buffer.append('\''); 311 } 312 buffer.append("}, isCritical="); 313 buffer.append(isCritical()); 314 buffer.append(')'); 315 } 316}