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 * https://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 /** the search future */ 061 private SearchFuture future; 062 063 /** wait time while polling for a SearchResponse */ 064 private long timeout; 065 066 /** time units of timeout value */ 067 private TimeUnit timeUnit; 068 069 /** a reference to hold the retrieved SearchResponse object from SearchFuture */ 070 private Response response; 071 072 /** the done flag */ 073 private boolean done; 074 075 /** a reference to hold the SearchResultDone response */ 076 private SearchResultDone searchDoneResp; 077 078 079 /** 080 * Instantiates a new search cursor. 081 * 082 * @param future the future 083 * @param timeout the timeout 084 * @param timeUnit the time unit 085 */ 086 public SearchCursorImpl( SearchFuture future, long timeout, TimeUnit timeUnit ) 087 { 088 if ( LOG_CURSOR.isDebugEnabled() ) 089 { 090 LOG_CURSOR.debug( I18n.msg( I18n.MSG_04170_CREATING_SEARCH_CURSOR, this ) ); 091 } 092 093 this.future = future; 094 this.timeout = timeout; 095 this.timeUnit = timeUnit; 096 } 097 098 099 /** 100 * {@inheritDoc} 101 */ 102 @Override 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 try 133 { 134 close( ldapException ); 135 } 136 catch ( IOException ioe ) 137 { 138 throw new LdapException( ioe.getMessage(), ioe ); 139 } 140 141 throw ldapException; 142 } 143 144 if ( response == null ) 145 { 146 future.cancel( true ); 147 148 throw new LdapConnectionTimeOutException( LdapNetworkConnection.TIME_OUT_ERROR ); 149 } 150 151 done = response instanceof SearchResultDone; 152 153 if ( done ) 154 { 155 searchDoneResp = ( SearchResultDone ) response; 156 response = null; 157 } 158 159 return !done; 160 } 161 162 163 /** 164 * {@inheritDoc} 165 */ 166 @Override 167 public Response get() throws InvalidCursorPositionException 168 { 169 if ( !available() ) 170 { 171 throw new InvalidCursorPositionException(); 172 } 173 174 return response; 175 } 176 177 178 /** 179 * {@inheritDoc} 180 */ 181 @Override 182 public SearchResultDone getSearchResultDone() 183 { 184 return searchDoneResp; 185 } 186 187 188 /** 189 * {@inheritDoc} 190 */ 191 @Override 192 public boolean available() 193 { 194 return response != null; 195 } 196 197 198 /** 199 * {@inheritDoc} 200 */ 201 @Override 202 public void close() throws IOException 203 { 204 if ( LOG_CURSOR.isDebugEnabled() ) 205 { 206 LOG_CURSOR.debug( I18n.msg( I18n.MSG_04171_CLOSING_SEARCH_CURSOR, this ) ); 207 } 208 209 close( null ); 210 } 211 212 213 /** 214 * {@inheritDoc} 215 */ 216 @Override 217 public void close( Exception cause ) throws IOException 218 { 219 if ( LOG_CURSOR.isDebugEnabled() ) 220 { 221 LOG_CURSOR.debug( I18n.msg( I18n.MSG_04171_CLOSING_SEARCH_CURSOR, this ) ); 222 } 223 224 if ( done ) 225 { 226 super.close(); 227 return; 228 } 229 230 if ( !future.isCancelled() ) 231 { 232 future.cancel( true ); 233 } 234 235 if ( cause != null ) 236 { 237 super.close( cause ); 238 } 239 else 240 { 241 super.close(); 242 } 243 } 244 245 246 // rest of all operations will throw UnsupportedOperationException 247 248 /** 249 * This operation is not supported in SearchCursor. 250 * {@inheritDoc} 251 */ 252 @Override 253 public void after( Response element ) throws LdapException, CursorException 254 { 255 throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName() 256 .concat( "." ).concat( "after( Response element )" ) ) ); 257 } 258 259 260 /** 261 * This operation is not supported in SearchCursor. 262 * {@inheritDoc} 263 */ 264 @Override 265 public void afterLast() throws LdapException, CursorException 266 { 267 throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName() 268 .concat( "." ).concat( "afterLast()" ) ) ); 269 } 270 271 272 /** 273 * This operation is not supported in SearchCursor. 274 * {@inheritDoc} 275 */ 276 @Override 277 public void before( Response element ) throws LdapException, CursorException 278 { 279 throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName() 280 .concat( "." ).concat( "before( Response element )" ) ) ); 281 } 282 283 284 /** 285 * This operation is not supported in SearchCursor. 286 * {@inheritDoc} 287 */ 288 @Override 289 public void beforeFirst() throws LdapException, CursorException 290 { 291 throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName() 292 .concat( "." ).concat( "beforeFirst()" ) ) ); 293 } 294 295 296 /** 297 * This operation is not supported in SearchCursor. 298 * {@inheritDoc} 299 */ 300 @Override 301 public boolean first() throws LdapException, CursorException 302 { 303 throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName() 304 .concat( "." ).concat( "first()" ) ) ); 305 } 306 307 308 /** 309 * This operation is not supported in SearchCursor. 310 * {@inheritDoc} 311 */ 312 @Override 313 public boolean last() throws LdapException, CursorException 314 { 315 throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName() 316 .concat( "." ).concat( "last()" ) ) ); 317 } 318 319 320 /** 321 * This operation is not supported in SearchCursor. 322 * {@inheritDoc} 323 */ 324 @Override 325 public boolean previous() throws LdapException, CursorException 326 { 327 throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName() 328 .concat( "." ).concat( "previous()" ) ) ); 329 } 330 331 332 /** 333 * {@inheritDoc} 334 */ 335 @Override 336 public boolean isDone() 337 { 338 return done; 339 } 340 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override 346 public boolean isReferral() 347 { 348 return response instanceof SearchResultReference; 349 } 350 351 352 /** 353 * {@inheritDoc} 354 */ 355 @Override 356 public Referral getReferral() throws LdapException 357 { 358 if ( isReferral() ) 359 { 360 return ( ( SearchResultReference ) response ).getReferral(); 361 } 362 363 throw new LdapException(); 364 } 365 366 367 /** 368 * {@inheritDoc} 369 */ 370 @Override 371 public boolean isEntry() 372 { 373 return response instanceof SearchResultEntry; 374 } 375 376 377 /** 378 * {@inheritDoc} 379 */ 380 @Override 381 public Entry getEntry() throws LdapException 382 { 383 if ( isEntry() ) 384 { 385 return ( ( SearchResultEntry ) response ).getEntry(); 386 } 387 388 if ( isReferral() ) 389 { 390 Referral referral = ( ( SearchResultReference ) response ).getReferral(); 391 throw new LdapReferralException( referral.getLdapUrls() ); 392 } 393 394 throw new LdapException(); 395 } 396 397 398 /** 399 * {@inheritDoc} 400 */ 401 @Override 402 public boolean isIntermediate() 403 { 404 return response instanceof IntermediateResponse; 405 } 406 407 408 /** 409 * {@inheritDoc} 410 */ 411 @Override 412 public IntermediateResponse getIntermediate() throws LdapException 413 { 414 if ( isEntry() ) 415 { 416 return ( IntermediateResponse ) response; 417 } 418 419 throw new LdapException(); 420 } 421}