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