View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     https://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  
21  package org.apache.directory.ldap.client.api;
22  
23  
24  import java.io.IOException;
25  import java.util.concurrent.TimeUnit;
26  
27  import org.apache.directory.api.i18n.I18n;
28  import org.apache.directory.api.ldap.model.constants.Loggers;
29  import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
30  import org.apache.directory.api.ldap.model.cursor.CursorException;
31  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
32  import org.apache.directory.api.ldap.model.cursor.SearchCursor;
33  import org.apache.directory.api.ldap.model.entry.Entry;
34  import org.apache.directory.api.ldap.model.exception.LdapException;
35  import org.apache.directory.api.ldap.model.exception.LdapReferralException;
36  import org.apache.directory.api.ldap.model.message.IntermediateResponse;
37  import org.apache.directory.api.ldap.model.message.Referral;
38  import org.apache.directory.api.ldap.model.message.Response;
39  import org.apache.directory.api.ldap.model.message.SearchResultDone;
40  import org.apache.directory.api.ldap.model.message.SearchResultEntry;
41  import org.apache.directory.api.ldap.model.message.SearchResultReference;
42  import org.apache.directory.ldap.client.api.exception.LdapConnectionTimeOutException;
43  import org.apache.directory.ldap.client.api.future.SearchFuture;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  
48  /**
49   * An implementation of Cursor based on the underlying SearchFuture instance.
50   * 
51   * Note: This is a forward only cursor hence the only valid operations are next(), get() and close() 
52   * 
53   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
54   */
55  public class SearchCursorImpl extends AbstractCursor<Response> implements SearchCursor
56  {
57      /** A dedicated log for cursors */
58      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
59  
60      /** the search future */
61      private SearchFuture future;
62  
63      /** wait time while polling for a SearchResponse */
64      private long timeout;
65  
66      /** time units of timeout value */
67      private TimeUnit timeUnit;
68  
69      /** a reference to hold the retrieved SearchResponse object from SearchFuture */
70      private Response response;
71  
72      /** the done flag */
73      private boolean done;
74  
75      /** a reference to hold the SearchResultDone response */
76      private SearchResultDone searchDoneResp;
77  
78  
79      /**
80       * Instantiates a new search cursor.
81       *
82       * @param future the future
83       * @param timeout the timeout
84       * @param timeUnit the time unit
85       */
86      public SearchCursorImpl( SearchFuture future, long timeout, TimeUnit timeUnit )
87      {
88          if ( LOG_CURSOR.isDebugEnabled() )
89          {
90              LOG_CURSOR.debug( I18n.msg( I18n.MSG_04170_CREATING_SEARCH_CURSOR, this ) );
91          }
92  
93          this.future = future;
94          this.timeout = timeout;
95          this.timeUnit = timeUnit;
96      }
97  
98  
99      /**
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 }