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   *     http://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      /** Speedup for logs */
61      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
62  
63      /** the search future */
64      private SearchFuture future;
65  
66      /** wait time while polling for a SearchResponse */
67      private long timeout;
68  
69      /** time units of timeout value */
70      private TimeUnit timeUnit;
71  
72      /** a reference to hold the retrieved SearchResponse object from SearchFuture */
73      private Response response;
74  
75      /** the done flag */
76      private boolean done;
77  
78      /** a reference to hold the SearchResultDone response */
79      private SearchResultDone searchDoneResp;
80  
81  
82      /**
83       * Instantiates a new search cursor.
84       *
85       * @param future the future
86       * @param timeout the timeout
87       * @param timeUnit the time unit
88       */
89      public SearchCursorImpl( SearchFuture future, long timeout, TimeUnit timeUnit )
90      {
91          if ( IS_DEBUG )
92          {
93              LOG_CURSOR.debug( "Creating SearchCursorImpl {}", this );
94          }
95  
96          this.future = future;
97          this.timeout = timeout;
98          this.timeUnit = timeUnit;
99      }
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 }