001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020 021package org.apache.directory.ldap.client.api; 022 023 024import java.util.concurrent.TimeUnit; 025 026import org.apache.directory.api.i18n.I18n; 027import org.apache.directory.api.ldap.model.constants.Loggers; 028import org.apache.directory.api.ldap.model.cursor.AbstractCursor; 029import org.apache.directory.api.ldap.model.cursor.CursorException; 030import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException; 031import org.apache.directory.api.ldap.model.cursor.SearchCursor; 032import org.apache.directory.api.ldap.model.entry.Entry; 033import org.apache.directory.api.ldap.model.exception.LdapException; 034import org.apache.directory.api.ldap.model.exception.LdapReferralException; 035import org.apache.directory.api.ldap.model.message.IntermediateResponse; 036import org.apache.directory.api.ldap.model.message.Referral; 037import org.apache.directory.api.ldap.model.message.Response; 038import org.apache.directory.api.ldap.model.message.SearchResultDone; 039import org.apache.directory.api.ldap.model.message.SearchResultEntry; 040import org.apache.directory.api.ldap.model.message.SearchResultReference; 041import org.apache.directory.ldap.client.api.future.SearchFuture; 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045 046/** 047 * An implementation of Cursor based on the underlying SearchFuture instance. 048 * 049 * Note: This is a forward only cursor hence the only valid operations are next(), get() and close() 050 * 051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 052 */ 053public class SearchCursorImpl extends AbstractCursor<Response> implements SearchCursor 054{ 055 /** A dedicated log for cursors */ 056 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 057 058 /** Speedup for logs */ 059 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 060 061 /** the search future */ 062 private SearchFuture future; 063 064 /** wait time while polling for a SearchResponse */ 065 private long timeout; 066 067 /** time units of timeout value */ 068 private TimeUnit timeUnit; 069 070 /** a reference to hold the retrieved SearchResponse object from SearchFuture */ 071 private Response response; 072 073 /** the done flag */ 074 private boolean done; 075 076 /** a reference to hold the SearchResultDone response */ 077 private SearchResultDone searchDoneResp; 078 079 080 /** 081 * Instantiates a new search cursor. 082 * 083 * @param future the future 084 * @param timeout the timeout 085 * @param timeUnit the time unit 086 */ 087 public SearchCursorImpl( SearchFuture future, long timeout, TimeUnit timeUnit ) 088 { 089 if ( IS_DEBUG ) 090 { 091 LOG_CURSOR.debug( "Creating SearchCursorImpl {}", this ); 092 } 093 094 this.future = future; 095 this.timeout = timeout; 096 this.timeUnit = timeUnit; 097 } 098 099 100 /** 101 * {@inheritDoc} 102 */ 103 public boolean next() throws LdapException, CursorException 104 { 105 if ( done ) 106 { 107 return false; 108 } 109 110 try 111 { 112 if ( future.isCancelled() ) 113 { 114 response = null; 115 done = true; 116 return false; 117 } 118 119 response = future.get( timeout, timeUnit ); 120 } 121 catch ( Exception e ) 122 { 123 LdapException ldapException = new LdapException( LdapNetworkConnection.NO_RESPONSE_ERROR, e ); 124 125 // Send an abandon request 126 if ( !future.isCancelled() ) 127 { 128 future.cancel( true ); 129 } 130 131 // close the cursor 132 close( ldapException ); 133 134 throw ldapException; 135 } 136 137 if ( response == null ) 138 { 139 future.cancel( true ); 140 141 throw new LdapException( LdapNetworkConnection.TIME_OUT_ERROR ); 142 } 143 144 done = ( response instanceof SearchResultDone ); 145 146 if ( done ) 147 { 148 searchDoneResp = ( SearchResultDone ) response; 149 150 // Process the response and throw an exception if needed 151 //ResultCodeEnum.processResponse( searchDoneResp ); 152 153 response = null; 154 } 155 156 return !done; 157 } 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 public Response get() throws InvalidCursorPositionException 164 { 165 if ( !available() ) 166 { 167 throw new InvalidCursorPositionException(); 168 } 169 170 return response; 171 } 172 173 174 /** 175 * {@inheritDoc} 176 */ 177 public SearchResultDone getSearchResultDone() 178 { 179 return searchDoneResp; 180 } 181 182 183 /** 184 * {@inheritDoc} 185 */ 186 public boolean available() 187 { 188 return response != null; 189 } 190 191 192 /** 193 * {@inheritDoc} 194 */ 195 @Override 196 public void close() 197 { 198 if ( IS_DEBUG ) 199 { 200 LOG_CURSOR.debug( "Closing SearchCursorImpl {}", this ); 201 } 202 203 close( null ); 204 } 205 206 207 /** 208 * {@inheritDoc} 209 */ 210 @Override 211 public void close( Exception cause ) 212 { 213 if ( IS_DEBUG ) 214 { 215 LOG_CURSOR.debug( "Closing SearchCursorImpl {}", this ); 216 } 217 218 if ( done ) 219 { 220 super.close(); 221 return; 222 } 223 224 if ( !future.isCancelled() ) 225 { 226 future.cancel( true ); 227 } 228 229 if ( cause != null ) 230 { 231 super.close( cause ); 232 } 233 else 234 { 235 super.close(); 236 } 237 } 238 239 240 // rest of all operations will throw UnsupportedOperationException 241 242 /** 243 * This operation is not supported in SearchCursor. 244 * {@inheritDoc} 245 */ 246 public void after( Response element ) throws LdapException, CursorException 247 { 248 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName() 249 .concat( "." ).concat( "after( Response element )" ) ) ); 250 } 251 252 253 /** 254 * This operation is not supported in SearchCursor. 255 * {@inheritDoc} 256 */ 257 public void afterLast() throws LdapException, CursorException 258 { 259 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName() 260 .concat( "." ).concat( "afterLast()" ) ) ); 261 } 262 263 264 /** 265 * This operation is not supported in SearchCursor. 266 * {@inheritDoc} 267 */ 268 public void before( Response element ) throws LdapException, CursorException 269 { 270 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName() 271 .concat( "." ).concat( "before( Response element )" ) ) ); 272 } 273 274 275 /** 276 * This operation is not supported in SearchCursor. 277 * {@inheritDoc} 278 */ 279 public void beforeFirst() throws LdapException, CursorException 280 { 281 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName() 282 .concat( "." ).concat( "beforeFirst()" ) ) ); 283 } 284 285 286 /** 287 * This operation is not supported in SearchCursor. 288 * {@inheritDoc} 289 */ 290 public boolean first() throws LdapException, CursorException 291 { 292 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName() 293 .concat( "." ).concat( "first()" ) ) ); 294 } 295 296 297 /** 298 * This operation is not supported in SearchCursor. 299 * {@inheritDoc} 300 */ 301 public boolean last() throws LdapException, CursorException 302 { 303 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName() 304 .concat( "." ).concat( "last()" ) ) ); 305 } 306 307 308 /** 309 * This operation is not supported in SearchCursor. 310 * {@inheritDoc} 311 */ 312 public boolean previous() throws LdapException, CursorException 313 { 314 throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName() 315 .concat( "." ).concat( "previous()" ) ) ); 316 } 317 318 319 /** 320 * {@inheritDoc} 321 */ 322 public boolean isDone() 323 { 324 return done; 325 } 326 327 328 /** 329 * {@inheritDoc} 330 */ 331 public boolean isReferral() 332 { 333 return response instanceof SearchResultReference; 334 } 335 336 337 /** 338 * {@inheritDoc} 339 */ 340 public Referral getReferral() throws LdapException 341 { 342 if ( isReferral() ) 343 { 344 return ( ( SearchResultReference ) response ).getReferral(); 345 } 346 347 throw new LdapException(); 348 } 349 350 351 /** 352 * {@inheritDoc} 353 */ 354 public boolean isEntry() 355 { 356 return response instanceof SearchResultEntry; 357 } 358 359 360 /** 361 * {@inheritDoc} 362 */ 363 public Entry getEntry() throws LdapException 364 { 365 if ( isEntry() ) 366 { 367 return ( ( SearchResultEntry ) response ).getEntry(); 368 } 369 370 if ( isReferral() ) 371 { 372 Referral referral = ( ( SearchResultReference ) response ).getReferral(); 373 throw new LdapReferralException( referral.getLdapUrls() ); 374 } 375 376 throw new LdapException(); 377 } 378 379 380 /** 381 * {@inheritDoc} 382 */ 383 public boolean isIntermediate() 384 { 385 return response instanceof IntermediateResponse; 386 } 387 388 389 /** 390 * {@inheritDoc} 391 */ 392 public IntermediateResponse getIntermediate() throws LdapException 393 { 394 if ( isEntry() ) 395 { 396 return ( IntermediateResponse ) response; 397 } 398 399 throw new LdapException(); 400 } 401}