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