001 /*
002 * Copyright 2009-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2016 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 */
021 package com.unboundid.ldap.protocol;
022
023
024
025 import java.util.ArrayList;
026 import java.util.Collections;
027 import java.util.Iterator;
028 import java.util.List;
029
030 import com.unboundid.asn1.ASN1Buffer;
031 import com.unboundid.asn1.ASN1BufferSequence;
032 import com.unboundid.asn1.ASN1Element;
033 import com.unboundid.asn1.ASN1Enumerated;
034 import com.unboundid.asn1.ASN1OctetString;
035 import com.unboundid.asn1.ASN1Sequence;
036 import com.unboundid.asn1.ASN1StreamReader;
037 import com.unboundid.asn1.ASN1StreamReaderSequence;
038 import com.unboundid.ldap.sdk.Control;
039 import com.unboundid.ldap.sdk.ExtendedResult;
040 import com.unboundid.ldap.sdk.LDAPException;
041 import com.unboundid.ldap.sdk.LDAPResult;
042 import com.unboundid.ldap.sdk.ResultCode;
043 import com.unboundid.util.NotMutable;
044 import com.unboundid.util.InternalUseOnly;
045 import com.unboundid.util.ThreadSafety;
046 import com.unboundid.util.ThreadSafetyLevel;
047
048 import static com.unboundid.ldap.protocol.ProtocolMessages.*;
049 import static com.unboundid.util.Debug.*;
050 import static com.unboundid.util.StaticUtils.*;
051 import static com.unboundid.util.Validator.*;
052
053
054
055 /**
056 * This class provides an implementation of a extended response protocol op.
057 */
058 @InternalUseOnly()
059 @NotMutable()
060 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
061 public final class ExtendedResponseProtocolOp
062 implements ProtocolOp
063 {
064 /**
065 * The BER type for the response OID element.
066 */
067 public static final byte TYPE_RESPONSE_OID = (byte) 0x8A;
068
069
070
071 /**
072 * The BER type for the response value element.
073 */
074 public static final byte TYPE_RESPONSE_VALUE = (byte) 0x8B;
075
076
077
078 /**
079 * The serial version UID for this serializable class.
080 */
081 private static final long serialVersionUID = -7757619031268544913L;
082
083
084
085 // The value for this extended response.
086 private final ASN1OctetString responseValue;
087
088 // The result code for this extended response.
089 private final int resultCode;
090
091 // The referral URLs for this extended response.
092 private final List<String> referralURLs;
093
094 // The diagnostic message for this extended response.
095 private final String diagnosticMessage;
096
097 // The matched DN for this extended response.
098 private final String matchedDN;
099
100 // The OID for this extended response.
101 private final String responseOID;
102
103
104
105 /**
106 * Creates a new instance of this extended response protocol op with the
107 * provided information.
108 *
109 * @param resultCode The result code for this response.
110 * @param matchedDN The matched DN for this response, if available.
111 * @param diagnosticMessage The diagnostic message for this response, if
112 * any.
113 * @param referralURLs The list of referral URLs for this response, if
114 * any.
115 * @param responseOID The response OID for this response, if any.
116 * @param responseValue The value for this response, if any.
117 */
118 public ExtendedResponseProtocolOp(final int resultCode,
119 final String matchedDN,
120 final String diagnosticMessage,
121 final List<String> referralURLs,
122 final String responseOID,
123 final ASN1OctetString responseValue)
124 {
125 this.resultCode = resultCode;
126 this.matchedDN = matchedDN;
127 this.diagnosticMessage = diagnosticMessage;
128 this.responseOID = responseOID;
129
130 if (referralURLs == null)
131 {
132 this.referralURLs = Collections.emptyList();
133 }
134 else
135 {
136 this.referralURLs = Collections.unmodifiableList(referralURLs);
137 }
138
139 if (responseValue == null)
140 {
141 this.responseValue = null;
142 }
143 else
144 {
145 this.responseValue =
146 new ASN1OctetString(TYPE_RESPONSE_VALUE, responseValue.getValue());
147 }
148 }
149
150
151
152 /**
153 * Creates a new extended response protocol op from the provided extended
154 * result object.
155 *
156 * @param result The extended result object to use to create this protocol
157 * op.
158 */
159 public ExtendedResponseProtocolOp(final LDAPResult result)
160 {
161 resultCode = result.getResultCode().intValue();
162 matchedDN = result.getMatchedDN();
163 diagnosticMessage = result.getDiagnosticMessage();
164 referralURLs = toList(result.getReferralURLs());
165
166 if (result instanceof ExtendedResult)
167 {
168 final ExtendedResult r = (ExtendedResult) result;
169 responseOID = r.getOID();
170 responseValue = r.getValue();
171 }
172 else
173 {
174 responseOID = null;
175 responseValue = null;
176 }
177 }
178
179
180
181 /**
182 * Creates a new extended response protocol op read from the provided ASN.1
183 * stream reader.
184 *
185 * @param reader The ASN.1 stream reader from which to read the extended
186 * response.
187 *
188 * @throws LDAPException If a problem occurs while reading or parsing the
189 * extended response.
190 */
191 ExtendedResponseProtocolOp(final ASN1StreamReader reader)
192 throws LDAPException
193 {
194 try
195 {
196 final ASN1StreamReaderSequence opSequence = reader.beginSequence();
197 resultCode = reader.readEnumerated();
198
199 String s = reader.readString();
200 ensureNotNull(s);
201 if (s.length() == 0)
202 {
203 matchedDN = null;
204 }
205 else
206 {
207 matchedDN = s;
208 }
209
210 s = reader.readString();
211 ensureNotNull(s);
212 if (s.length() == 0)
213 {
214 diagnosticMessage = null;
215 }
216 else
217 {
218 diagnosticMessage = s;
219 }
220
221 ASN1OctetString value = null;
222 String oid = null;
223 final ArrayList<String> refs = new ArrayList<String>(1);
224 while (opSequence.hasMoreElements())
225 {
226 final byte type = (byte) reader.peek();
227 if (type == GenericResponseProtocolOp.TYPE_REFERRALS)
228 {
229 final ASN1StreamReaderSequence refSequence = reader.beginSequence();
230 while (refSequence.hasMoreElements())
231 {
232 refs.add(reader.readString());
233 }
234 }
235 else if (type == TYPE_RESPONSE_OID)
236 {
237 oid = reader.readString();
238 }
239 else if (type == TYPE_RESPONSE_VALUE)
240 {
241 value = new ASN1OctetString(type, reader.readBytes());
242 }
243 else
244 {
245 throw new LDAPException(ResultCode.DECODING_ERROR,
246 ERR_EXTENDED_RESPONSE_INVALID_ELEMENT.get(toHex(type)));
247 }
248 }
249
250 referralURLs = Collections.unmodifiableList(refs);
251 responseOID = oid;
252 responseValue = value;
253 }
254 catch (LDAPException le)
255 {
256 debugException(le);
257 throw le;
258 }
259 catch (Exception e)
260 {
261 debugException(e);
262 throw new LDAPException(ResultCode.DECODING_ERROR,
263 ERR_EXTENDED_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
264 }
265 }
266
267
268
269 /**
270 * Retrieves the result code for this extended response.
271 *
272 * @return The result code for this extended response.
273 */
274 public int getResultCode()
275 {
276 return resultCode;
277 }
278
279
280
281 /**
282 * Retrieves the matched DN for this extended response, if any.
283 *
284 * @return The matched DN for this extended response, or {@code null} if
285 * there is no matched DN.
286 */
287 public String getMatchedDN()
288 {
289 return matchedDN;
290 }
291
292
293
294 /**
295 * Retrieves the diagnostic message for this extended response, if any.
296 *
297 * @return The diagnostic message for this extended response, or {@code null}
298 * if there is no diagnostic message.
299 */
300 public String getDiagnosticMessage()
301 {
302 return diagnosticMessage;
303 }
304
305
306
307 /**
308 * Retrieves the list of referral URLs for this extended response.
309 *
310 * @return The list of referral URLs for this extended response, or an empty
311 * list if there are no referral URLs.
312 */
313 public List<String> getReferralURLs()
314 {
315 return referralURLs;
316 }
317
318
319
320 /**
321 * Retrieves the OID for this extended response, if any.
322 *
323 * @return The OID for this extended response, or {@code null} if there is no
324 * response OID.
325 */
326 public String getResponseOID()
327 {
328 return responseOID;
329 }
330
331
332
333 /**
334 * Retrieves the value for this extended response, if any.
335 *
336 * @return The value for this extended response, or {@code null} if there is
337 * no response value.
338 */
339 public ASN1OctetString getResponseValue()
340 {
341 return responseValue;
342 }
343
344
345
346 /**
347 * {@inheritDoc}
348 */
349 public byte getProtocolOpType()
350 {
351 return LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE;
352 }
353
354
355
356 /**
357 * {@inheritDoc}
358 */
359 public ASN1Element encodeProtocolOp()
360 {
361 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(6);
362 elements.add(new ASN1Enumerated(getResultCode()));
363
364 final String mdn = getMatchedDN();
365 if (mdn == null)
366 {
367 elements.add(new ASN1OctetString());
368 }
369 else
370 {
371 elements.add(new ASN1OctetString(mdn));
372 }
373
374 final String dm = getDiagnosticMessage();
375 if (dm == null)
376 {
377 elements.add(new ASN1OctetString());
378 }
379 else
380 {
381 elements.add(new ASN1OctetString(dm));
382 }
383
384 final List<String> refs = getReferralURLs();
385 if (! refs.isEmpty())
386 {
387 final ArrayList<ASN1Element> refElements =
388 new ArrayList<ASN1Element>(refs.size());
389 for (final String r : refs)
390 {
391 refElements.add(new ASN1OctetString(r));
392 }
393 elements.add(new ASN1Sequence(GenericResponseProtocolOp.TYPE_REFERRALS,
394 refElements));
395 }
396
397 if (responseOID != null)
398 {
399 elements.add(new ASN1OctetString(TYPE_RESPONSE_OID, responseOID));
400 }
401
402 if (responseValue != null)
403 {
404 elements.add(responseValue);
405 }
406
407 return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE,
408 elements);
409 }
410
411
412
413 /**
414 * Decodes the provided ASN.1 element as an extended response protocol op.
415 *
416 * @param element The ASN.1 element to be decoded.
417 *
418 * @return The decoded extended response protocol op.
419 *
420 * @throws LDAPException If the provided ASN.1 element cannot be decoded as
421 * an extended response protocol op.
422 */
423 public static ExtendedResponseProtocolOp decodeProtocolOp(
424 final ASN1Element element)
425 throws LDAPException
426 {
427 try
428 {
429 final ASN1Element[] elements =
430 ASN1Sequence.decodeAsSequence(element).elements();
431 final int resultCode =
432 ASN1Enumerated.decodeAsEnumerated(elements[0]).intValue();
433
434 final String matchedDN;
435 final String md =
436 ASN1OctetString.decodeAsOctetString(elements[1]).stringValue();
437 if (md.length() > 0)
438 {
439 matchedDN = md;
440 }
441 else
442 {
443 matchedDN = null;
444 }
445
446 final String diagnosticMessage;
447 final String dm =
448 ASN1OctetString.decodeAsOctetString(elements[2]).stringValue();
449 if (dm.length() > 0)
450 {
451 diagnosticMessage = dm;
452 }
453 else
454 {
455 diagnosticMessage = null;
456 }
457
458 ASN1OctetString responseValue = null;
459 List<String> referralURLs = null;
460 String responseOID = null;
461 if (elements.length > 3)
462 {
463 for (int i=3; i < elements.length; i++)
464 {
465 switch (elements[i].getType())
466 {
467 case GenericResponseProtocolOp.TYPE_REFERRALS:
468 final ASN1Element[] refElements =
469 ASN1Sequence.decodeAsSequence(elements[3]).elements();
470 referralURLs = new ArrayList<String>(refElements.length);
471 for (final ASN1Element e : refElements)
472 {
473 referralURLs.add(
474 ASN1OctetString.decodeAsOctetString(e).stringValue());
475 }
476 break;
477
478 case TYPE_RESPONSE_OID:
479 responseOID = ASN1OctetString.decodeAsOctetString(elements[i]).
480 stringValue();
481 break;
482
483 case TYPE_RESPONSE_VALUE:
484 responseValue = ASN1OctetString.decodeAsOctetString(elements[i]);
485 break;
486
487 default:
488 throw new LDAPException(ResultCode.DECODING_ERROR,
489 ERR_EXTENDED_RESPONSE_INVALID_ELEMENT.get(
490 toHex(elements[i].getType())));
491 }
492 }
493 }
494
495 return new ExtendedResponseProtocolOp(resultCode, matchedDN,
496 diagnosticMessage, referralURLs, responseOID, responseValue);
497 }
498 catch (final LDAPException le)
499 {
500 debugException(le);
501 throw le;
502 }
503 catch (final Exception e)
504 {
505 debugException(e);
506 throw new LDAPException(ResultCode.DECODING_ERROR,
507 ERR_EXTENDED_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)),
508 e);
509 }
510 }
511
512
513
514 /**
515 * {@inheritDoc}
516 */
517 public void writeTo(final ASN1Buffer buffer)
518 {
519 final ASN1BufferSequence opSequence =
520 buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE);
521 buffer.addEnumerated(resultCode);
522 buffer.addOctetString(matchedDN);
523 buffer.addOctetString(diagnosticMessage);
524
525 if (! referralURLs.isEmpty())
526 {
527 final ASN1BufferSequence refSequence =
528 buffer.beginSequence(GenericResponseProtocolOp.TYPE_REFERRALS);
529 for (final String s : referralURLs)
530 {
531 buffer.addOctetString(s);
532 }
533 refSequence.end();
534 }
535
536 if (responseOID != null)
537 {
538 buffer.addOctetString(TYPE_RESPONSE_OID, responseOID);
539 }
540
541 if (responseValue != null)
542 {
543 buffer.addOctetString(TYPE_RESPONSE_VALUE, responseValue.getValue());
544 }
545
546 opSequence.end();
547 }
548
549
550
551 /**
552 * Creates a extended result from this protocol op.
553 *
554 * @param controls The set of controls to include in the extended result.
555 * It may be empty or {@code null} if no controls should be
556 * included.
557 *
558 * @return The extended result that was created.
559 */
560 public ExtendedResult toExtendedResult(final Control... controls)
561 {
562 final String[] referralArray = new String[referralURLs.size()];
563 referralURLs.toArray(referralArray);
564
565 return new ExtendedResult(-1, ResultCode.valueOf(resultCode),
566 diagnosticMessage, matchedDN, referralArray, responseOID,
567 responseValue, controls);
568 }
569
570
571
572 /**
573 * Retrieves a string representation of this protocol op.
574 *
575 * @return A string representation of this protocol op.
576 */
577 @Override()
578 public String toString()
579 {
580 final StringBuilder buffer = new StringBuilder();
581 toString(buffer);
582 return buffer.toString();
583 }
584
585
586
587 /**
588 * {@inheritDoc}
589 */
590 public void toString(final StringBuilder buffer)
591 {
592 buffer.append("ExtendedResponseProtocolOp(resultCode=");
593 buffer.append(resultCode);
594
595 if (matchedDN != null)
596 {
597 buffer.append(", matchedDN='");
598 buffer.append(matchedDN);
599 buffer.append('\'');
600 }
601
602 if (diagnosticMessage != null)
603 {
604 buffer.append(", diagnosticMessage='");
605 buffer.append(diagnosticMessage);
606 buffer.append('\'');
607 }
608
609 if (! referralURLs.isEmpty())
610 {
611 buffer.append(", referralURLs={");
612
613 final Iterator<String> iterator = referralURLs.iterator();
614 while (iterator.hasNext())
615 {
616 buffer.append('\'');
617 buffer.append(iterator.next());
618 buffer.append('\'');
619 if (iterator.hasNext())
620 {
621 buffer.append(',');
622 }
623 }
624
625 buffer.append('}');
626 }
627
628 if (responseOID != null)
629 {
630 buffer.append(", responseOID='");
631 buffer.append(responseOID);
632 buffer.append('\'');
633 }
634
635 buffer.append(')');
636 }
637 }