001 /*
002 * Copyright 2007-2012 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2012 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.sdk;
022
023
024
025 import java.util.Timer;
026 import java.util.concurrent.LinkedBlockingQueue;
027 import java.util.concurrent.TimeUnit;
028
029 import com.unboundid.asn1.ASN1Buffer;
030 import com.unboundid.asn1.ASN1BufferSequence;
031 import com.unboundid.asn1.ASN1Element;
032 import com.unboundid.asn1.ASN1OctetString;
033 import com.unboundid.asn1.ASN1Sequence;
034 import com.unboundid.ldap.protocol.LDAPMessage;
035 import com.unboundid.ldap.protocol.LDAPResponse;
036 import com.unboundid.ldap.protocol.ProtocolOp;
037 import com.unboundid.util.InternalUseOnly;
038 import com.unboundid.util.Mutable;
039 import com.unboundid.util.ThreadSafety;
040 import com.unboundid.util.ThreadSafetyLevel;
041
042 import static com.unboundid.ldap.sdk.LDAPMessages.*;
043 import static com.unboundid.util.Debug.*;
044 import static com.unboundid.util.StaticUtils.*;
045 import static com.unboundid.util.Validator.*;
046
047
048
049 /**
050 * This class implements the processing necessary to perform an LDAPv3 compare
051 * operation, which may be used to determine whether a specified entry contains
052 * a given attribute value. Compare requests include the DN of the target
053 * entry, the name of the target attribute, and the value for which to make the
054 * determination. It may also include a set of controls to send to the server.
055 * <BR><BR>
056 * The assertion value may be specified as either a string or a byte array. If
057 * it is specified as a byte array, then it may represent either a binary or a
058 * string value. If a string value is provided as a byte array, then it should
059 * use the UTF-8 encoding for that value.
060 * <BR><BR>
061 * {@code CompareRequest} objects are mutable and therefore can be altered and
062 * re-used for multiple requests. Note, however, that {@code CompareRequest}
063 * objects are not threadsafe and therefore a single {@code CompareRequest}
064 * object instance should not be used to process multiple requests at the same
065 * time.
066 * <BR><BR>
067 * <H2>Example</H2>
068 * The following example demonstrates the process for performing a compare
069 * operation:
070 * <PRE>
071 * CompareRequest compareRequest =
072 * new CompareRequest("dc=example,dc=com", "description", "test");
073 *
074 * try
075 * {
076 * CompareResult compareResult = connection.compare(compareRequest);
077 *
078 * // The compare operation didn't throw an exception, so we can try to
079 * // determine whether the compare matched.
080 * if (compareResult.compareMatched())
081 * {
082 * System.out.println("The entry does have a description value of test");
083 * }
084 * else
085 * {
086 * System.out.println("The entry does not have a description value of " +
087 * "test");
088 * }
089 * }
090 * catch (LDAPException le)
091 * {
092 * System.err.println("The compare operation failed.");
093 * }
094 * </PRE>
095 */
096 @Mutable()
097 @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
098 public final class CompareRequest
099 extends UpdatableLDAPRequest
100 implements ReadOnlyCompareRequest, ResponseAcceptor, ProtocolOp
101 {
102 /**
103 * The serial version UID for this serializable class.
104 */
105 private static final long serialVersionUID = 6343453776330347024L;
106
107
108
109 // The queue that will be used to receive response messages from the server.
110 private final LinkedBlockingQueue<LDAPResponse> responseQueue =
111 new LinkedBlockingQueue<LDAPResponse>();
112
113 // The assertion value for this compare request.
114 private ASN1OctetString assertionValue;
115
116 // The message ID from the last LDAP message sent from this request.
117 private int messageID = -1;
118
119 // The name of the target attribute.
120 private String attributeName;
121
122 // The DN of the entry in which the comparison is to be performed.
123 private String dn;
124
125
126
127 /**
128 * Creates a new compare request with the provided information.
129 *
130 * @param dn The DN of the entry in which the comparison is to
131 * be performed. It must not be {@code null}.
132 * @param attributeName The name of the target attribute for which the
133 * comparison is to be performed. It must not be
134 * {@code null}.
135 * @param assertionValue The assertion value to verify within the entry. It
136 * must not be {@code null}.
137 */
138 public CompareRequest(final String dn, final String attributeName,
139 final String assertionValue)
140 {
141 super(null);
142
143 ensureNotNull(dn, attributeName, assertionValue);
144
145 this.dn = dn;
146 this.attributeName = attributeName;
147 this.assertionValue = new ASN1OctetString(assertionValue);
148 }
149
150
151
152 /**
153 * Creates a new compare request with the provided information.
154 *
155 * @param dn The DN of the entry in which the comparison is to
156 * be performed. It must not be {@code null}.
157 * @param attributeName The name of the target attribute for which the
158 * comparison is to be performed. It must not be
159 * {@code null}.
160 * @param assertionValue The assertion value to verify within the entry. It
161 * must not be {@code null}.
162 */
163 public CompareRequest(final String dn, final String attributeName,
164 final byte[] assertionValue)
165 {
166 super(null);
167
168 ensureNotNull(dn, attributeName, assertionValue);
169
170 this.dn = dn;
171 this.attributeName = attributeName;
172 this.assertionValue = new ASN1OctetString(assertionValue);
173 }
174
175
176
177 /**
178 * Creates a new compare request with the provided information.
179 *
180 * @param dn The DN of the entry in which the comparison is to
181 * be performed. It must not be {@code null}.
182 * @param attributeName The name of the target attribute for which the
183 * comparison is to be performed. It must not be
184 * {@code null}.
185 * @param assertionValue The assertion value to verify within the entry. It
186 * must not be {@code null}.
187 */
188 public CompareRequest(final DN dn, final String attributeName,
189 final String assertionValue)
190 {
191 super(null);
192
193 ensureNotNull(dn, attributeName, assertionValue);
194
195 this.dn = dn.toString();
196 this.attributeName = attributeName;
197 this.assertionValue = new ASN1OctetString(assertionValue);
198 }
199
200
201
202 /**
203 * Creates a new compare request with the provided information.
204 *
205 * @param dn The DN of the entry in which the comparison is to
206 * be performed. It must not be {@code null}.
207 * @param attributeName The name of the target attribute for which the
208 * comparison is to be performed. It must not be
209 * {@code null}.
210 * @param assertionValue The assertion value to verify within the entry. It
211 * must not be {@code null}.
212 */
213 public CompareRequest(final DN dn, final String attributeName,
214 final byte[] assertionValue)
215 {
216 super(null);
217
218 ensureNotNull(dn, attributeName, assertionValue);
219
220 this.dn = dn.toString();
221 this.attributeName = attributeName;
222 this.assertionValue = new ASN1OctetString(assertionValue);
223 }
224
225
226
227 /**
228 * Creates a new compare request with the provided information.
229 *
230 * @param dn The DN of the entry in which the comparison is to
231 * be performed. It must not be {@code null}.
232 * @param attributeName The name of the target attribute for which the
233 * comparison is to be performed. It must not be
234 * {@code null}.
235 * @param assertionValue The assertion value to verify within the entry. It
236 * must not be {@code null}.
237 * @param controls The set of controls for this compare request.
238 */
239 public CompareRequest(final String dn, final String attributeName,
240 final String assertionValue, final Control[] controls)
241 {
242 super(controls);
243
244 ensureNotNull(dn, attributeName, assertionValue);
245
246 this.dn = dn;
247 this.attributeName = attributeName;
248 this.assertionValue = new ASN1OctetString(assertionValue);
249 }
250
251
252
253 /**
254 * Creates a new compare request with the provided information.
255 *
256 * @param dn The DN of the entry in which the comparison is to
257 * be performed. It must not be {@code null}.
258 * @param attributeName The name of the target attribute for which the
259 * comparison is to be performed. It must not be
260 * {@code null}.
261 * @param assertionValue The assertion value to verify within the entry. It
262 * must not be {@code null}.
263 * @param controls The set of controls for this compare request.
264 */
265 public CompareRequest(final String dn, final String attributeName,
266 final byte[] assertionValue, final Control[] controls)
267 {
268 super(controls);
269
270 ensureNotNull(dn, attributeName, assertionValue);
271
272 this.dn = dn;
273 this.attributeName = attributeName;
274 this.assertionValue = new ASN1OctetString(assertionValue);
275 }
276
277
278
279 /**
280 * Creates a new compare request with the provided information.
281 *
282 * @param dn The DN of the entry in which the comparison is to
283 * be performed. It must not be {@code null}.
284 * @param attributeName The name of the target attribute for which the
285 * comparison is to be performed. It must not be
286 * {@code null}.
287 * @param assertionValue The assertion value to verify within the entry. It
288 * must not be {@code null}.
289 * @param controls The set of controls for this compare request.
290 */
291 public CompareRequest(final DN dn, final String attributeName,
292 final String assertionValue, final Control[] controls)
293 {
294 super(controls);
295
296 ensureNotNull(dn, attributeName, assertionValue);
297
298 this.dn = dn.toString();
299 this.attributeName = attributeName;
300 this.assertionValue = new ASN1OctetString(assertionValue);
301 }
302
303
304
305 /**
306 * Creates a new compare request with the provided information.
307 *
308 * @param dn The DN of the entry in which the comparison is to
309 * be performed. It must not be {@code null}.
310 * @param attributeName The name of the target attribute for which the
311 * comparison is to be performed. It must not be
312 * {@code null}.
313 * @param assertionValue The assertion value to verify within the entry. It
314 * must not be {@code null}.
315 * @param controls The set of controls for this compare request.
316 */
317 public CompareRequest(final DN dn, final String attributeName,
318 final ASN1OctetString assertionValue,
319 final Control[] controls)
320 {
321 super(controls);
322
323 ensureNotNull(dn, attributeName, assertionValue);
324
325 this.dn = dn.toString();
326 this.attributeName = attributeName;
327 this.assertionValue = assertionValue;
328 }
329
330
331
332 /**
333 * Creates a new compare request with the provided information.
334 *
335 * @param dn The DN of the entry in which the comparison is to
336 * be performed. It must not be {@code null}.
337 * @param attributeName The name of the target attribute for which the
338 * comparison is to be performed. It must not be
339 * {@code null}.
340 * @param assertionValue The assertion value to verify within the entry. It
341 * must not be {@code null}.
342 * @param controls The set of controls for this compare request.
343 */
344 public CompareRequest(final DN dn, final String attributeName,
345 final byte[] assertionValue, final Control[] controls)
346 {
347 super(controls);
348
349 ensureNotNull(dn, attributeName, assertionValue);
350
351 this.dn = dn.toString();
352 this.attributeName = attributeName;
353 this.assertionValue = new ASN1OctetString(assertionValue);
354 }
355
356
357
358 /**
359 * {@inheritDoc}
360 */
361 public String getDN()
362 {
363 return dn;
364 }
365
366
367
368 /**
369 * Specifies the DN of the entry in which the comparison is to be performed.
370 *
371 * @param dn The DN of the entry in which the comparison is to be performed.
372 * It must not be {@code null}.
373 */
374 public void setDN(final String dn)
375 {
376 ensureNotNull(dn);
377
378 this.dn = dn;
379 }
380
381
382
383 /**
384 * Specifies the DN of the entry in which the comparison is to be performed.
385 *
386 * @param dn The DN of the entry in which the comparison is to be performed.
387 * It must not be {@code null}.
388 */
389 public void setDN(final DN dn)
390 {
391 ensureNotNull(dn);
392
393 this.dn = dn.toString();
394 }
395
396
397
398 /**
399 * {@inheritDoc}
400 */
401 public String getAttributeName()
402 {
403 return attributeName;
404 }
405
406
407
408 /**
409 * Specifies the name of the attribute for which the comparison is to be
410 * performed.
411 *
412 * @param attributeName The name of the attribute for which the comparison
413 * is to be performed. It must not be {@code null}.
414 */
415 public void setAttributeName(final String attributeName)
416 {
417 ensureNotNull(attributeName);
418
419 this.attributeName = attributeName;
420 }
421
422
423
424 /**
425 * {@inheritDoc}
426 */
427 public String getAssertionValue()
428 {
429 return assertionValue.stringValue();
430 }
431
432
433
434 /**
435 * {@inheritDoc}
436 */
437 public byte[] getAssertionValueBytes()
438 {
439 return assertionValue.getValue();
440 }
441
442
443
444 /**
445 * {@inheritDoc}
446 */
447 public ASN1OctetString getRawAssertionValue()
448 {
449 return assertionValue;
450 }
451
452
453
454 /**
455 * Specifies the assertion value to specify within the target entry.
456 *
457 * @param assertionValue The assertion value to specify within the target
458 * entry. It must not be {@code null}.
459 */
460 public void setAssertionValue(final String assertionValue)
461 {
462 ensureNotNull(assertionValue);
463
464 this.assertionValue = new ASN1OctetString(assertionValue);
465 }
466
467
468
469 /**
470 * Specifies the assertion value to specify within the target entry.
471 *
472 * @param assertionValue The assertion value to specify within the target
473 * entry. It must not be {@code null}.
474 */
475 public void setAssertionValue(final byte[] assertionValue)
476 {
477 ensureNotNull(assertionValue);
478
479 this.assertionValue = new ASN1OctetString(assertionValue);
480 }
481
482
483
484 /**
485 * Specifies the assertion value to specify within the target entry.
486 *
487 * @param assertionValue The assertion value to specify within the target
488 * entry. It must not be {@code null}.
489 */
490 public void setAssertionValue(final ASN1OctetString assertionValue)
491 {
492 this.assertionValue = assertionValue;
493 }
494
495
496
497 /**
498 * {@inheritDoc}
499 */
500 public byte getProtocolOpType()
501 {
502 return LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_REQUEST;
503 }
504
505
506
507 /**
508 * {@inheritDoc}
509 */
510 public void writeTo(final ASN1Buffer buffer)
511 {
512 final ASN1BufferSequence requestSequence =
513 buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_REQUEST);
514 buffer.addOctetString(dn);
515
516 final ASN1BufferSequence avaSequence = buffer.beginSequence();
517 buffer.addOctetString(attributeName);
518 buffer.addElement(assertionValue);
519 avaSequence.end();
520 requestSequence.end();
521 }
522
523
524
525 /**
526 * Encodes the compare request protocol op to an ASN.1 element.
527 *
528 * @return The ASN.1 element with the encoded compare request protocol op.
529 */
530 ASN1Element encodeProtocolOp()
531 {
532 // Create the compare request protocol op.
533 final ASN1Element[] avaElements =
534 {
535 new ASN1OctetString(attributeName),
536 assertionValue
537 };
538
539 final ASN1Element[] protocolOpElements =
540 {
541 new ASN1OctetString(dn),
542 new ASN1Sequence(avaElements)
543 };
544
545 return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_COMPARE_REQUEST,
546 protocolOpElements);
547 }
548
549
550
551 /**
552 * Sends this delete request to the directory server over the provided
553 * connection and returns the associated response.
554 *
555 * @param connection The connection to use to communicate with the directory
556 * server.
557 * @param depth The current referral depth for this request. It should
558 * always be one for the initial request, and should only
559 * be incremented when following referrals.
560 *
561 * @return An LDAP result object that provides information about the result
562 * of the delete processing.
563 *
564 * @throws LDAPException If a problem occurs while sending the request or
565 * reading the response.
566 */
567 @Override()
568 protected CompareResult process(final LDAPConnection connection,
569 final int depth)
570 throws LDAPException
571 {
572 if (connection.synchronousMode())
573 {
574 return processSync(connection, depth);
575 }
576
577 final long requestTime = System.nanoTime();
578 processAsync(connection, null);
579
580 try
581 {
582 // Wait for and process the response.
583 final LDAPResponse response;
584 try
585 {
586 final long responseTimeout = getResponseTimeoutMillis(connection);
587 if (responseTimeout > 0)
588 {
589 response = responseQueue.poll(responseTimeout, TimeUnit.MILLISECONDS);
590 }
591 else
592 {
593 response = responseQueue.take();
594 }
595 }
596 catch (InterruptedException ie)
597 {
598 debugException(ie);
599 throw new LDAPException(ResultCode.LOCAL_ERROR,
600 ERR_COMPARE_INTERRUPTED.get(connection.getHostPort()), ie);
601 }
602
603 return handleResponse(connection, response, requestTime, depth);
604 }
605 finally
606 {
607 connection.deregisterResponseAcceptor(messageID);
608 }
609 }
610
611
612
613 /**
614 * Sends this compare request to the directory server over the provided
615 * connection and returns the message ID for the request.
616 *
617 * @param connection The connection to use to communicate with the
618 * directory server.
619 * @param resultListener The async result listener that is to be notified
620 * when the response is received. It may be
621 * {@code null} only if the result is to be processed
622 * by this class.
623 *
624 * @return The async request ID created for the operation, or {@code null} if
625 * the provided {@code resultListener} is {@code null} and the
626 * operation will not actually be processed asynchronously.
627 *
628 * @throws LDAPException If a problem occurs while sending the request.
629 */
630 AsyncRequestID processAsync(final LDAPConnection connection,
631 final AsyncCompareResultListener resultListener)
632 throws LDAPException
633 {
634 // Create the LDAP message.
635 messageID = connection.nextMessageID();
636 final LDAPMessage message = new LDAPMessage(messageID, this, getControls());
637
638
639 // If the provided async result listener is {@code null}, then we'll use
640 // this class as the message acceptor. Otherwise, create an async helper
641 // and use it as the message acceptor.
642 final AsyncRequestID asyncRequestID;
643 if (resultListener == null)
644 {
645 asyncRequestID = null;
646 connection.registerResponseAcceptor(messageID, this);
647 }
648 else
649 {
650 final AsyncCompareHelper compareHelper =
651 new AsyncCompareHelper(connection, messageID, resultListener,
652 getIntermediateResponseListener());
653 connection.registerResponseAcceptor(messageID, compareHelper);
654 asyncRequestID = compareHelper.getAsyncRequestID();
655
656 final long timeout = getResponseTimeoutMillis(connection);
657 if (timeout > 0L)
658 {
659 final Timer timer = connection.getTimer();
660 final AsyncTimeoutTimerTask timerTask =
661 new AsyncTimeoutTimerTask(compareHelper);
662 timer.schedule(timerTask, timeout);
663 asyncRequestID.setTimerTask(timerTask);
664 }
665 }
666
667
668 // Send the request to the server.
669 try
670 {
671 debugLDAPRequest(this);
672 connection.getConnectionStatistics().incrementNumCompareRequests();
673 connection.sendMessage(message);
674 return asyncRequestID;
675 }
676 catch (LDAPException le)
677 {
678 debugException(le);
679
680 connection.deregisterResponseAcceptor(messageID);
681 throw le;
682 }
683 }
684
685
686
687 /**
688 * Processes this compare operation in synchronous mode, in which the same
689 * thread will send the request and read the response.
690 *
691 * @param connection The connection to use to communicate with the directory
692 * server.
693 * @param depth The current referral depth for this request. It should
694 * always be one for the initial request, and should only
695 * be incremented when following referrals.
696 *
697 * @return An LDAP result object that provides information about the result
698 * of the compare processing.
699 *
700 * @throws LDAPException If a problem occurs while sending the request or
701 * reading the response.
702 */
703 private CompareResult processSync(final LDAPConnection connection,
704 final int depth)
705 throws LDAPException
706 {
707 // Create the LDAP message.
708 messageID = connection.nextMessageID();
709 final LDAPMessage message =
710 new LDAPMessage(messageID, this, getControls());
711
712
713 // Set the appropriate timeout on the socket.
714 try
715 {
716 connection.getConnectionInternals().getSocket().setSoTimeout(
717 (int) getResponseTimeoutMillis(connection));
718 }
719 catch (Exception e)
720 {
721 debugException(e);
722 }
723
724
725 // Send the request to the server.
726 final long requestTime = System.nanoTime();
727 debugLDAPRequest(this);
728 connection.getConnectionStatistics().incrementNumCompareRequests();
729 connection.sendMessage(message);
730
731 while (true)
732 {
733 final LDAPResponse response;
734 try
735 {
736 response = connection.readResponse(messageID);
737 }
738 catch (final LDAPException le)
739 {
740 debugException(le);
741
742 if ((le.getResultCode() == ResultCode.TIMEOUT) &&
743 connection.getConnectionOptions().abandonOnTimeout())
744 {
745 connection.abandon(messageID);
746 }
747
748 throw le;
749 }
750
751 if (response instanceof IntermediateResponse)
752 {
753 final IntermediateResponseListener listener =
754 getIntermediateResponseListener();
755 if (listener != null)
756 {
757 listener.intermediateResponseReturned(
758 (IntermediateResponse) response);
759 }
760 }
761 else
762 {
763 return handleResponse(connection, response, requestTime, depth);
764 }
765 }
766 }
767
768
769
770 /**
771 * Performs the necessary processing for handling a response.
772 *
773 * @param connection The connection used to read the response.
774 * @param response The response to be processed.
775 * @param requestTime The time the request was sent to the server.
776 * @param depth The current referral depth for this request. It
777 * should always be one for the initial request, and
778 * should only be incremented when following referrals.
779 *
780 * @return The compare result.
781 *
782 * @throws LDAPException If a problem occurs.
783 */
784 private CompareResult handleResponse(final LDAPConnection connection,
785 final LDAPResponse response,
786 final long requestTime, final int depth)
787 throws LDAPException
788 {
789 if (response == null)
790 {
791 final long waitTime = nanosToMillis(System.nanoTime() - requestTime);
792 if (connection.getConnectionOptions().abandonOnTimeout())
793 {
794 connection.abandon(messageID);
795 }
796
797 throw new LDAPException(ResultCode.TIMEOUT,
798 ERR_COMPARE_CLIENT_TIMEOUT.get(waitTime, connection.getHostPort()));
799 }
800
801 connection.getConnectionStatistics().incrementNumCompareResponses(
802 System.nanoTime() - requestTime);
803 if (response instanceof ConnectionClosedResponse)
804 {
805 // The connection was closed while waiting for the response.
806 final ConnectionClosedResponse ccr = (ConnectionClosedResponse) response;
807 final String message = ccr.getMessage();
808 if (message == null)
809 {
810 throw new LDAPException(ccr.getResultCode(),
811 ERR_CONN_CLOSED_WAITING_FOR_COMPARE_RESPONSE.get(
812 connection.getHostPort(), toString()));
813 }
814 else
815 {
816 throw new LDAPException(ccr.getResultCode(),
817 ERR_CONN_CLOSED_WAITING_FOR_COMPARE_RESPONSE_WITH_MESSAGE.get(
818 connection.getHostPort(), toString(), message));
819 }
820 }
821
822 final CompareResult result;
823 if (response instanceof CompareResult)
824 {
825 result = (CompareResult) response;
826 }
827 else
828 {
829 result = new CompareResult((LDAPResult) response);
830 }
831
832 if ((result.getResultCode().equals(ResultCode.REFERRAL)) &&
833 followReferrals(connection))
834 {
835 if (depth >= connection.getConnectionOptions().getReferralHopLimit())
836 {
837 return new CompareResult(messageID,
838 ResultCode.REFERRAL_LIMIT_EXCEEDED,
839 ERR_TOO_MANY_REFERRALS.get(),
840 result.getMatchedDN(),
841 result.getReferralURLs(),
842 result.getResponseControls());
843 }
844
845 return followReferral(result, connection, depth);
846 }
847 else
848 {
849 return result;
850 }
851 }
852
853
854
855 /**
856 * Attempts to follow a referral to perform a compare operation in the target
857 * server.
858 *
859 * @param referralResult The LDAP result object containing information about
860 * the referral to follow.
861 * @param connection The connection on which the referral was received.
862 * @param depth The number of referrals followed in the course of
863 * processing this request.
864 *
865 * @return The result of attempting to process the compare operation by
866 * following the referral.
867 *
868 * @throws LDAPException If a problem occurs while attempting to establish
869 * the referral connection, sending the request, or
870 * reading the result.
871 */
872 private CompareResult followReferral(final CompareResult referralResult,
873 final LDAPConnection connection,
874 final int depth)
875 throws LDAPException
876 {
877 for (final String urlString : referralResult.getReferralURLs())
878 {
879 try
880 {
881 final LDAPURL referralURL = new LDAPURL(urlString);
882 final String host = referralURL.getHost();
883
884 if (host == null)
885 {
886 // We can't handle a referral in which there is no host.
887 continue;
888 }
889
890 final CompareRequest compareRequest;
891 if (referralURL.baseDNProvided())
892 {
893 compareRequest = new CompareRequest(referralURL.getBaseDN(),
894 attributeName, assertionValue,
895 getControls());
896 }
897 else
898 {
899 compareRequest = this;
900 }
901
902 final LDAPConnection referralConn = connection.getReferralConnector().
903 getReferralConnection(referralURL, connection);
904 try
905 {
906 return compareRequest.process(referralConn, depth+1);
907 }
908 finally
909 {
910 referralConn.setDisconnectInfo(DisconnectType.REFERRAL, null, null);
911 referralConn.close();
912 }
913 }
914 catch (LDAPException le)
915 {
916 debugException(le);
917 }
918 }
919
920 // If we've gotten here, then we could not follow any of the referral URLs,
921 // so we'll just return the original referral result.
922 return referralResult;
923 }
924
925
926
927 /**
928 * {@inheritDoc}
929 */
930 @InternalUseOnly()
931 public void responseReceived(final LDAPResponse response)
932 throws LDAPException
933 {
934 try
935 {
936 responseQueue.put(response);
937 }
938 catch (Exception e)
939 {
940 debugException(e);
941 throw new LDAPException(ResultCode.LOCAL_ERROR,
942 ERR_EXCEPTION_HANDLING_RESPONSE.get(getExceptionMessage(e)), e);
943 }
944 }
945
946
947
948 /**
949 * {@inheritDoc}
950 */
951 @Override()
952 public int getLastMessageID()
953 {
954 return messageID;
955 }
956
957
958
959 /**
960 * {@inheritDoc}
961 */
962 @Override()
963 public OperationType getOperationType()
964 {
965 return OperationType.COMPARE;
966 }
967
968
969
970 /**
971 * {@inheritDoc}
972 */
973 public CompareRequest duplicate()
974 {
975 return duplicate(getControls());
976 }
977
978
979
980 /**
981 * {@inheritDoc}
982 */
983 public CompareRequest duplicate(final Control[] controls)
984 {
985 final CompareRequest r = new CompareRequest(dn, attributeName,
986 assertionValue.getValue(), controls);
987
988 if (followReferralsInternal() != null)
989 {
990 r.setFollowReferrals(followReferralsInternal());
991 }
992
993 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
994
995 return r;
996 }
997
998
999
1000 /**
1001 * {@inheritDoc}
1002 */
1003 @Override()
1004 public void toString(final StringBuilder buffer)
1005 {
1006 buffer.append("CompareRequest(dn='");
1007 buffer.append(dn);
1008 buffer.append("', attr='");
1009 buffer.append(attributeName);
1010 buffer.append("', value='");
1011 buffer.append(assertionValue.stringValue());
1012 buffer.append('\'');
1013
1014 final Control[] controls = getControls();
1015 if (controls.length > 0)
1016 {
1017 buffer.append(", controls={");
1018 for (int i=0; i < controls.length; i++)
1019 {
1020 if (i > 0)
1021 {
1022 buffer.append(", ");
1023 }
1024
1025 buffer.append(controls[i]);
1026 }
1027 buffer.append('}');
1028 }
1029
1030 buffer.append(')');
1031 }
1032 }