001/* 002 * Copyright 2009-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2009-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.protocol; 037 038 039 040import java.util.ArrayList; 041import java.util.Collections; 042import java.util.Iterator; 043import java.util.List; 044 045import com.unboundid.asn1.ASN1Buffer; 046import com.unboundid.asn1.ASN1BufferSequence; 047import com.unboundid.asn1.ASN1StreamReader; 048import com.unboundid.asn1.ASN1StreamReaderSequence; 049import com.unboundid.ldap.sdk.Control; 050import com.unboundid.ldap.sdk.LDAPException; 051import com.unboundid.ldap.sdk.LDAPResult; 052import com.unboundid.ldap.sdk.ResultCode; 053import com.unboundid.util.Debug; 054import com.unboundid.util.InternalUseOnly; 055import com.unboundid.util.NotExtensible; 056import com.unboundid.util.NotNull; 057import com.unboundid.util.Nullable; 058import com.unboundid.util.StaticUtils; 059import com.unboundid.util.ThreadSafety; 060import com.unboundid.util.ThreadSafetyLevel; 061import com.unboundid.util.Validator; 062 063import static com.unboundid.ldap.protocol.ProtocolMessages.*; 064 065 066 067/** 068 * This class provides an implementation of a generic response protocol op. 069 * It must be subclassed by classes providing implementations for each 070 * operation type. 071 */ 072@InternalUseOnly() 073@NotExtensible() 074@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 075public abstract class GenericResponseProtocolOp 076 implements ProtocolOp 077{ 078 /** 079 * The BER type for the referral URLs elements. 080 */ 081 public static final byte TYPE_REFERRALS = (byte) 0xA3; 082 083 084 085 /** 086 * The serial version UID for this serializable class. 087 */ 088 private static final long serialVersionUID = 3837308973105414874L; 089 090 091 092 // The BER type for this response. 093 private final byte type; 094 095 // The result code for this response. 096 private final int resultCode; 097 098 // The referral URLs for this response. 099 @NotNull private final List<String> referralURLs; 100 101 // The diagnostic message for this response. 102 @Nullable private final String diagnosticMessage; 103 104 // The matched DN for this response.Static 105 @Nullable private final String matchedDN; 106 107 108 109 /** 110 * Creates a new instance of this response with the provided information. 111 * 112 * @param type The BER type for this response. 113 * @param resultCode The result code for this response. 114 * @param matchedDN The matched DN for this result, if available. 115 * @param diagnosticMessage The diagnostic message for this response, if 116 * available. 117 * @param referralURLs The list of referral URLs for this response, if 118 * available. 119 */ 120 protected GenericResponseProtocolOp(final byte type, final int resultCode, 121 @Nullable final String matchedDN, 122 @Nullable final String diagnosticMessage, 123 @Nullable final List<String> referralURLs) 124 { 125 this.type = type; 126 this.resultCode = resultCode; 127 this.matchedDN = matchedDN; 128 this.diagnosticMessage = diagnosticMessage; 129 130 if (referralURLs == null) 131 { 132 this.referralURLs = Collections.emptyList(); 133 } 134 else 135 { 136 this.referralURLs = Collections.unmodifiableList(referralURLs); 137 } 138 } 139 140 141 142 /** 143 * Creates a new response read from the provided ASN.1 stream reader. 144 * 145 * @param reader The ASN.1 stream reader from which to read the response. 146 * 147 * @throws LDAPException If a problem occurs while reading or parsing the 148 * response. 149 */ 150 protected GenericResponseProtocolOp(@NotNull final ASN1StreamReader reader) 151 throws LDAPException 152 { 153 try 154 { 155 type = (byte) reader.peek(); 156 final ASN1StreamReaderSequence opSequence = reader.beginSequence(); 157 resultCode = reader.readEnumerated(); 158 159 String s = reader.readString(); 160 Validator.ensureNotNull(s); 161 if (s.isEmpty()) 162 { 163 matchedDN = null; 164 } 165 else 166 { 167 matchedDN = s; 168 } 169 170 s = reader.readString(); 171 Validator.ensureNotNull(s); 172 if (s.isEmpty()) 173 { 174 diagnosticMessage = null; 175 } 176 else 177 { 178 diagnosticMessage = s; 179 } 180 181 if (opSequence.hasMoreElements()) 182 { 183 final ArrayList<String> refs = new ArrayList<>(1); 184 final ASN1StreamReaderSequence refSequence = reader.beginSequence(); 185 while (refSequence.hasMoreElements()) 186 { 187 refs.add(reader.readString()); 188 } 189 referralURLs = Collections.unmodifiableList(refs); 190 } 191 else 192 { 193 referralURLs = Collections.emptyList(); 194 } 195 } 196 catch (final Exception e) 197 { 198 Debug.debugException(e); 199 throw new LDAPException(ResultCode.DECODING_ERROR, 200 ERR_RESPONSE_CANNOT_DECODE.get( 201 StaticUtils.getExceptionMessage(e)), e); 202 } 203 } 204 205 206 207 /** 208 * Retrieves the result code for this response. 209 * 210 * @return The result code for this response. 211 */ 212 public final int getResultCode() 213 { 214 return resultCode; 215 } 216 217 218 219 /** 220 * Retrieves the matched DN for this response, if any. 221 * 222 * @return The matched DN for this response, or {@code null} if there is 223 * no matched DN. 224 */ 225 @Nullable() 226 public final String getMatchedDN() 227 { 228 return matchedDN; 229 } 230 231 232 233 /** 234 * Retrieves the diagnostic message for this response, if any. 235 * 236 * @return The diagnostic message for this response, or {@code null} if there 237 * is no diagnostic message. 238 */ 239 @Nullable() 240 public final String getDiagnosticMessage() 241 { 242 return diagnosticMessage; 243 } 244 245 246 247 /** 248 * Retrieves the list of referral URLs for this response. 249 * 250 * @return The list of referral URLs for this response, or an empty list 251 * if there are no referral URLs. 252 */ 253 @NotNull() 254 public final List<String> getReferralURLs() 255 { 256 return referralURLs; 257 } 258 259 260 261 /** 262 * {@inheritDoc} 263 */ 264 @Override() 265 public byte getProtocolOpType() 266 { 267 return type; 268 } 269 270 271 272 /** 273 * {@inheritDoc} 274 */ 275 @Override() 276 public final void writeTo(@NotNull final ASN1Buffer buffer) 277 { 278 final ASN1BufferSequence opSequence = buffer.beginSequence(type); 279 buffer.addEnumerated(resultCode); 280 buffer.addOctetString(matchedDN); 281 buffer.addOctetString(diagnosticMessage); 282 283 if (! referralURLs.isEmpty()) 284 { 285 final ASN1BufferSequence refSequence = 286 buffer.beginSequence(TYPE_REFERRALS); 287 for (final String s : referralURLs) 288 { 289 buffer.addOctetString(s); 290 } 291 refSequence.end(); 292 } 293 opSequence.end(); 294 } 295 296 297 298 /** 299 * Creates a new LDAP result object from this response protocol op. 300 * 301 * @param controls The set of controls to include in the LDAP result. It 302 * may be empty or {@code null} if no controls should be 303 * included. 304 * 305 * @return The LDAP result that was created. 306 */ 307 @NotNull() 308 public LDAPResult toLDAPResult(@Nullable final Control... controls) 309 { 310 final String[] refs; 311 if (referralURLs.isEmpty()) 312 { 313 refs = StaticUtils.NO_STRINGS; 314 } 315 else 316 { 317 refs = new String[referralURLs.size()]; 318 referralURLs.toArray(refs); 319 } 320 321 return new LDAPResult(-1, ResultCode.valueOf(resultCode), diagnosticMessage, 322 matchedDN, refs, controls); 323 } 324 325 326 327 /** 328 * Retrieves a string representation of this protocol op. 329 * 330 * @return A string representation of this protocol op. 331 */ 332 @Override() 333 @NotNull() 334 public final String toString() 335 { 336 final StringBuilder buffer = new StringBuilder(); 337 toString(buffer); 338 return buffer.toString(); 339 } 340 341 342 343 /** 344 * {@inheritDoc} 345 */ 346 @Override() 347 public final void toString(@NotNull final StringBuilder buffer) 348 { 349 buffer.append("ResponseProtocolOp(type="); 350 StaticUtils.toHex(type, buffer); 351 buffer.append(", resultCode="); 352 buffer.append(resultCode); 353 354 if (matchedDN != null) 355 { 356 buffer.append(", matchedDN='"); 357 buffer.append(matchedDN); 358 buffer.append('\''); 359 } 360 361 if (diagnosticMessage != null) 362 { 363 buffer.append(", diagnosticMessage='"); 364 buffer.append(diagnosticMessage); 365 buffer.append('\''); 366 } 367 368 if (! referralURLs.isEmpty()) 369 { 370 buffer.append(", referralURLs={"); 371 372 final Iterator<String> iterator = referralURLs.iterator(); 373 while (iterator.hasNext()) 374 { 375 buffer.append('\''); 376 buffer.append(iterator.next()); 377 buffer.append('\''); 378 if (iterator.hasNext()) 379 { 380 buffer.append(','); 381 } 382 } 383 384 buffer.append('}'); 385 } 386 buffer.append(')'); 387 } 388}