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