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