001/* 002 * Copyright 2009-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-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 */ 021package com.unboundid.ldap.sdk.migrate.ldapjdk; 022 023 024 025import java.util.Enumeration; 026import java.util.NoSuchElementException; 027import java.util.concurrent.LinkedBlockingQueue; 028import java.util.concurrent.TimeUnit; 029import java.util.concurrent.atomic.AtomicBoolean; 030import java.util.concurrent.atomic.AtomicInteger; 031import java.util.concurrent.atomic.AtomicReference; 032 033import com.unboundid.ldap.sdk.AsyncRequestID; 034import com.unboundid.ldap.sdk.AsyncSearchResultListener; 035import com.unboundid.ldap.sdk.Control; 036import com.unboundid.ldap.sdk.ResultCode; 037import com.unboundid.ldap.sdk.SearchResult; 038import com.unboundid.ldap.sdk.SearchResultEntry; 039import com.unboundid.ldap.sdk.SearchResultReference; 040import com.unboundid.util.InternalUseOnly; 041import com.unboundid.util.Mutable; 042import com.unboundid.util.NotExtensible; 043import com.unboundid.util.ThreadSafety; 044import com.unboundid.util.ThreadSafetyLevel; 045 046import static com.unboundid.util.Debug.*; 047 048 049 050/** 051 * This class provides a data structure that provides access to data returned 052 * in response to a search operation. 053 * <BR><BR> 054 * This class is primarily intended to be used in the process of updating 055 * applications which use the Netscape Directory SDK for Java to switch to or 056 * coexist with the UnboundID LDAP SDK for Java. For applications not written 057 * using the Netscape Directory SDK for Java, the {@link SearchResult} class 058 * should be used instead. 059 */ 060@Mutable() 061@NotExtensible() 062@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 063public class LDAPSearchResults 064 implements Enumeration<Object>, AsyncSearchResultListener 065{ 066 /** 067 * The serial version UID for this serializable class. 068 */ 069 private static final long serialVersionUID = 7884355145560496230L; 070 071 072 073 // Indicates whether the end of the result set has been reached. 074 private final AtomicBoolean searchDone; 075 076 // The number of items that can be read immediately without blocking. 077 private final AtomicInteger count; 078 079 // The set of controls for the last result element returned. 080 private final AtomicReference<Control[]> lastControls; 081 082 // The next object to be returned. 083 private final AtomicReference<Object> nextResult; 084 085 // The search result done message for the search. 086 private final AtomicReference<SearchResult> searchResult; 087 088 // The maximum length of time in milliseconds to wait for a response. 089 private final long maxWaitTime; 090 091 // The queue used to hold results. 092 private final LinkedBlockingQueue<Object> resultQueue; 093 094 095 096 /** 097 * Creates a new LDAP search results object. 098 */ 099 public LDAPSearchResults() 100 { 101 this(0L); 102 } 103 104 105 106 /** 107 * Creates a new LDAP search results object with the specified maximum wait 108 * time. 109 * 110 * @param maxWaitTime The maximum wait time in milliseconds. 111 */ 112 public LDAPSearchResults(final long maxWaitTime) 113 { 114 this.maxWaitTime = maxWaitTime; 115 116 searchDone = new AtomicBoolean(false); 117 count = new AtomicInteger(0); 118 lastControls = new AtomicReference<Control[]>(); 119 nextResult = new AtomicReference<Object>(); 120 searchResult = new AtomicReference<SearchResult>(); 121 resultQueue = new LinkedBlockingQueue<Object>(50); 122 } 123 124 125 126 /** 127 * Retrieves the next object returned from the server, if possible. When this 128 * method returns, then the {@code nextResult} reference will also contain the 129 * object that was returned. 130 * 131 * @return The next object returned from the server, or {@code null} if there 132 * are no more objects to return. 133 */ 134 private Object nextObject() 135 { 136 Object o = nextResult.get(); 137 if (o != null) 138 { 139 return o; 140 } 141 142 o = resultQueue.poll(); 143 if (o != null) 144 { 145 nextResult.set(o); 146 return o; 147 } 148 149 if (searchDone.get()) 150 { 151 return null; 152 } 153 154 try 155 { 156 if (maxWaitTime > 0) 157 { 158 o = resultQueue.poll(maxWaitTime, TimeUnit.MILLISECONDS); 159 if (o == null) 160 { 161 o = new SearchResult(-1, ResultCode.TIMEOUT, null, null, null, 0, 0, 162 null); 163 count.incrementAndGet(); 164 } 165 } 166 else 167 { 168 o = resultQueue.take(); 169 } 170 } 171 catch (Exception e) 172 { 173 debugException(e); 174 175 o = new SearchResult(-1, ResultCode.USER_CANCELED, null, null, null, 0, 0, 176 null); 177 count.incrementAndGet(); 178 } 179 180 nextResult.set(o); 181 return o; 182 } 183 184 185 186 /** 187 * Indicates whether there are any more search results to return. 188 * 189 * @return {@code true} if there are more search results to return, or 190 * {@code false} if not. 191 */ 192 public boolean hasMoreElements() 193 { 194 final Object o = nextObject(); 195 if (o == null) 196 { 197 return false; 198 } 199 200 if (o instanceof SearchResult) 201 { 202 final SearchResult r = (SearchResult) o; 203 if (r.getResultCode().equals(ResultCode.SUCCESS)) 204 { 205 lastControls.set(r.getResponseControls()); 206 searchDone.set(true); 207 nextResult.set(null); 208 return false; 209 } 210 } 211 212 return true; 213 } 214 215 216 217 /** 218 * Retrieves the next element in the set of search results. 219 * 220 * @return The next element in the set of search results. 221 * 222 * @throws NoSuchElementException If there are no more results. 223 */ 224 public Object nextElement() 225 throws NoSuchElementException 226 { 227 final Object o = nextObject(); 228 if (o == null) 229 { 230 throw new NoSuchElementException(); 231 } 232 233 nextResult.set(null); 234 count.decrementAndGet(); 235 236 if (o instanceof SearchResultEntry) 237 { 238 final SearchResultEntry e = (SearchResultEntry) o; 239 lastControls.set(e.getControls()); 240 return new LDAPEntry(e); 241 } 242 else if (o instanceof SearchResultReference) 243 { 244 final SearchResultReference r = (SearchResultReference) o; 245 lastControls.set(r.getControls()); 246 return new LDAPReferralException(r); 247 } 248 else 249 { 250 final SearchResult r = (SearchResult) o; 251 searchDone.set(true); 252 nextResult.set(null); 253 lastControls.set(r.getResponseControls()); 254 return new LDAPException(r.getDiagnosticMessage(), 255 r.getResultCode().intValue(), r.getDiagnosticMessage(), 256 r.getMatchedDN()); 257 } 258 } 259 260 261 262 /** 263 * Retrieves the next entry from the set of search results. 264 * 265 * @return The next entry from the set of search results. 266 * 267 * @throws LDAPException If there are no more elements to return, or if 268 * the next element in the set of results is not an 269 * entry. 270 */ 271 public LDAPEntry next() 272 throws LDAPException 273 { 274 if (! hasMoreElements()) 275 { 276 throw new LDAPException(null, ResultCode.NO_RESULTS_RETURNED_INT_VALUE); 277 } 278 279 final Object o = nextElement(); 280 if (o instanceof LDAPEntry) 281 { 282 return (LDAPEntry) o; 283 } 284 285 throw (LDAPException) o; 286 } 287 288 289 290 /** 291 * Retrieves the number of results that are available for immediate 292 * processing. 293 * 294 * @return The number of results that are available for immediate processing. 295 */ 296 public int getCount() 297 { 298 return count.get(); 299 } 300 301 302 303 /** 304 * Retrieves the response controls for the last result element returned, or 305 * for the search itself if the search has completed. 306 * 307 * @return The response controls for the last result element returned, or 308 * {@code null} if no elements have yet been returned or if the last 309 * element did not include any controls. 310 */ 311 public LDAPControl[] getResponseControls() 312 { 313 final Control[] controls = lastControls.get(); 314 if ((controls == null) || (controls.length == 0)) 315 { 316 return null; 317 } 318 319 return LDAPControl.toLDAPControls(controls); 320 } 321 322 323 324 /** 325 * {@inheritDoc} 326 */ 327 @InternalUseOnly() 328 public void searchEntryReturned(final SearchResultEntry searchEntry) 329 { 330 if (searchDone.get()) 331 { 332 return; 333 } 334 335 try 336 { 337 resultQueue.put(searchEntry); 338 count.incrementAndGet(); 339 } 340 catch (Exception e) 341 { 342 // This should never happen. 343 debugException(e); 344 searchDone.set(true); 345 } 346 } 347 348 349 350 /** 351 * {@inheritDoc} 352 */ 353 @InternalUseOnly() 354 public void searchReferenceReturned( 355 final SearchResultReference searchReference) 356 { 357 if (searchDone.get()) 358 { 359 return; 360 } 361 362 try 363 { 364 resultQueue.put(searchReference); 365 count.incrementAndGet(); 366 } 367 catch (Exception e) 368 { 369 // This should never happen. 370 debugException(e); 371 searchDone.set(true); 372 } 373 } 374 375 376 377 /** 378 * Indicates that the provided search result has been received in response to 379 * an asynchronous search operation. Note that automatic referral following 380 * is not supported for asynchronous operations, so it is possible that this 381 * result could include a referral. 382 * 383 * @param requestID The async request ID of the request for which the 384 * response was received. 385 * @param searchResult The search result that has been received. 386 */ 387 @InternalUseOnly() 388 public void searchResultReceived(final AsyncRequestID requestID, 389 final SearchResult searchResult) 390 { 391 if (searchDone.get()) 392 { 393 return; 394 } 395 396 try 397 { 398 resultQueue.put(searchResult); 399 if (! searchResult.getResultCode().equals(ResultCode.SUCCESS)) 400 { 401 count.incrementAndGet(); 402 } 403 } 404 catch (Exception e) 405 { 406 // This should never happen. 407 debugException(e); 408 searchDone.set(true); 409 } 410 } 411}