001/* 002 * Copyright 2009-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2009-2020 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-2020 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.StaticUtils; 057import com.unboundid.util.ThreadSafety; 058import com.unboundid.util.ThreadSafetyLevel; 059import com.unboundid.util.Validator; 060 061import static com.unboundid.ldap.protocol.ProtocolMessages.*; 062 063 064 065/** 066 * This class provides an implementation of a generic response protocol op. 067 * It must be subclassed by classes providing implementations for each 068 * operation type. 069 */ 070@InternalUseOnly() 071@NotExtensible() 072@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 073public abstract class GenericResponseProtocolOp 074 implements ProtocolOp 075{ 076 /** 077 * The BER type for the referral URLs elements. 078 */ 079 public static final byte TYPE_REFERRALS = (byte) 0xA3; 080 081 082 083 /** 084 * The serial version UID for this serializable class. 085 */ 086 private static final long serialVersionUID = 3837308973105414874L; 087 088 089 090 // The BER type for this response. 091 private final byte type; 092 093 // The result code for this response. 094 private final int resultCode; 095 096 // The referral URLs for this response. 097 private final List<String> referralURLs; 098 099 // The diagnostic message for this response. 100 private final String diagnosticMessage; 101 102 // The matched DN for this response.Static 103 private final String matchedDN; 104 105 106 107 /** 108 * Creates a new instance of this response with the provided information. 109 * 110 * @param type The BER type for this response. 111 * @param resultCode The result code for this response. 112 * @param matchedDN The matched DN for this result, if available. 113 * @param diagnosticMessage The diagnostic message for this response, if 114 * available. 115 * @param referralURLs The list of referral URLs for this response, if 116 * available. 117 */ 118 protected GenericResponseProtocolOp(final byte type, final int resultCode, 119 final String matchedDN, 120 final String diagnosticMessage, 121 final List<String> referralURLs) 122 { 123 this.type = type; 124 this.resultCode = resultCode; 125 this.matchedDN = matchedDN; 126 this.diagnosticMessage = diagnosticMessage; 127 128 if (referralURLs == null) 129 { 130 this.referralURLs = Collections.emptyList(); 131 } 132 else 133 { 134 this.referralURLs = Collections.unmodifiableList(referralURLs); 135 } 136 } 137 138 139 140 /** 141 * Creates a new response read from the provided ASN.1 stream reader. 142 * 143 * @param reader The ASN.1 stream reader from which to read the response. 144 * 145 * @throws LDAPException If a problem occurs while reading or parsing the 146 * response. 147 */ 148 protected GenericResponseProtocolOp(final ASN1StreamReader reader) 149 throws LDAPException 150 { 151 try 152 { 153 type = (byte) reader.peek(); 154 final ASN1StreamReaderSequence opSequence = reader.beginSequence(); 155 resultCode = reader.readEnumerated(); 156 157 String s = reader.readString(); 158 Validator.ensureNotNull(s); 159 if (s.isEmpty()) 160 { 161 matchedDN = null; 162 } 163 else 164 { 165 matchedDN = s; 166 } 167 168 s = reader.readString(); 169 Validator.ensureNotNull(s); 170 if (s.isEmpty()) 171 { 172 diagnosticMessage = null; 173 } 174 else 175 { 176 diagnosticMessage = s; 177 } 178 179 if (opSequence.hasMoreElements()) 180 { 181 final ArrayList<String> refs = new ArrayList<>(1); 182 final ASN1StreamReaderSequence refSequence = reader.beginSequence(); 183 while (refSequence.hasMoreElements()) 184 { 185 refs.add(reader.readString()); 186 } 187 referralURLs = Collections.unmodifiableList(refs); 188 } 189 else 190 { 191 referralURLs = Collections.emptyList(); 192 } 193 } 194 catch (final Exception e) 195 { 196 Debug.debugException(e); 197 throw new LDAPException(ResultCode.DECODING_ERROR, 198 ERR_RESPONSE_CANNOT_DECODE.get( 199 StaticUtils.getExceptionMessage(e)), e); 200 } 201 } 202 203 204 205 /** 206 * Retrieves the result code for this response. 207 * 208 * @return The result code for this response. 209 */ 210 public final int getResultCode() 211 { 212 return resultCode; 213 } 214 215 216 217 /** 218 * Retrieves the matched DN for this response, if any. 219 * 220 * @return The matched DN for this response, or {@code null} if there is 221 * no matched DN. 222 */ 223 public final String getMatchedDN() 224 { 225 return matchedDN; 226 } 227 228 229 230 /** 231 * Retrieves the diagnostic message for this response, if any. 232 * 233 * @return The diagnostic message for this response, or {@code null} if there 234 * is no diagnostic message. 235 */ 236 public final String getDiagnosticMessage() 237 { 238 return diagnosticMessage; 239 } 240 241 242 243 /** 244 * Retrieves the list of referral URLs for this response. 245 * 246 * @return The list of referral URLs for this response, or an empty list 247 * if there are no referral URLs. 248 */ 249 public final List<String> getReferralURLs() 250 { 251 return referralURLs; 252 } 253 254 255 256 /** 257 * {@inheritDoc} 258 */ 259 @Override() 260 public byte getProtocolOpType() 261 { 262 return type; 263 } 264 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 @Override() 271 public final void writeTo(final ASN1Buffer buffer) 272 { 273 final ASN1BufferSequence opSequence = buffer.beginSequence(type); 274 buffer.addEnumerated(resultCode); 275 buffer.addOctetString(matchedDN); 276 buffer.addOctetString(diagnosticMessage); 277 278 if (! referralURLs.isEmpty()) 279 { 280 final ASN1BufferSequence refSequence = 281 buffer.beginSequence(TYPE_REFERRALS); 282 for (final String s : referralURLs) 283 { 284 buffer.addOctetString(s); 285 } 286 refSequence.end(); 287 } 288 opSequence.end(); 289 } 290 291 292 293 /** 294 * Creates a new LDAP result object from this response protocol op. 295 * 296 * @param controls The set of controls to include in the LDAP result. It 297 * may be empty or {@code null} if no controls should be 298 * included. 299 * 300 * @return The LDAP result that was created. 301 */ 302 public LDAPResult toLDAPResult(final Control... controls) 303 { 304 final String[] refs; 305 if (referralURLs.isEmpty()) 306 { 307 refs = StaticUtils.NO_STRINGS; 308 } 309 else 310 { 311 refs = new String[referralURLs.size()]; 312 referralURLs.toArray(refs); 313 } 314 315 return new LDAPResult(-1, ResultCode.valueOf(resultCode), diagnosticMessage, 316 matchedDN, refs, controls); 317 } 318 319 320 321 /** 322 * Retrieves a string representation of this protocol op. 323 * 324 * @return A string representation of this protocol op. 325 */ 326 @Override() 327 public final String toString() 328 { 329 final StringBuilder buffer = new StringBuilder(); 330 toString(buffer); 331 return buffer.toString(); 332 } 333 334 335 336 /** 337 * {@inheritDoc} 338 */ 339 @Override() 340 public final void toString(final StringBuilder buffer) 341 { 342 buffer.append("ResponseProtocolOp(type="); 343 StaticUtils.toHex(type, buffer); 344 buffer.append(", resultCode="); 345 buffer.append(resultCode); 346 347 if (matchedDN != null) 348 { 349 buffer.append(", matchedDN='"); 350 buffer.append(matchedDN); 351 buffer.append('\''); 352 } 353 354 if (diagnosticMessage != null) 355 { 356 buffer.append(", diagnosticMessage='"); 357 buffer.append(diagnosticMessage); 358 buffer.append('\''); 359 } 360 361 if (! referralURLs.isEmpty()) 362 { 363 buffer.append(", referralURLs={"); 364 365 final Iterator<String> iterator = referralURLs.iterator(); 366 while (iterator.hasNext()) 367 { 368 buffer.append('\''); 369 buffer.append(iterator.next()); 370 buffer.append('\''); 371 if (iterator.hasNext()) 372 { 373 buffer.append(','); 374 } 375 } 376 377 buffer.append('}'); 378 } 379 buffer.append(')'); 380 } 381}