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.util.concurrent.TimeUnit;
25  
26  import org.apache.directory.api.i18n.I18n;
27  import org.apache.directory.api.ldap.model.constants.Loggers;
28  import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
29  import org.apache.directory.api.ldap.model.cursor.CursorException;
30  import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
31  import org.apache.directory.api.ldap.model.cursor.SearchCursor;
32  import org.apache.directory.api.ldap.model.entry.Entry;
33  import org.apache.directory.api.ldap.model.exception.LdapException;
34  import org.apache.directory.api.ldap.model.exception.LdapReferralException;
35  import org.apache.directory.api.ldap.model.message.IntermediateResponse;
36  import org.apache.directory.api.ldap.model.message.Referral;
37  import org.apache.directory.api.ldap.model.message.Response;
38  import org.apache.directory.api.ldap.model.message.SearchResultDone;
39  import org.apache.directory.api.ldap.model.message.SearchResultEntry;
40  import org.apache.directory.api.ldap.model.message.SearchResultReference;
41  import org.apache.directory.ldap.client.api.future.SearchFuture;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  
46  /**
47   * An implementation of Cursor based on the underlying SearchFuture instance.
48   * 
49   * Note: This is a forward only cursor hence the only valid operations are next(), get() and close() 
50   * 
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   */
53  public class SearchCursorImpl extends AbstractCursor<Response> implements SearchCursor
54  {
55      /** A dedicated log for cursors */
56      private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
57  
58      /** Speedup for logs */
59      private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
60  
61      /** the search future */
62      private SearchFuture future;
63  
64      /** wait time while polling for a SearchResponse */
65      private long timeout;
66  
67      /** time units of timeout value */
68      private TimeUnit timeUnit;
69  
70      /** a reference to hold the retrieved SearchResponse object from SearchFuture */
71      private Response response;
72  
73      /** the done flag */
74      private boolean done;
75  
76      /** a reference to hold the SearchResultDone response */
77      private SearchResultDone searchDoneResp;
78  
79  
80      /**
81       * Instantiates a new search cursor.
82       *
83       * @param future the future
84       * @param timeout the timeout
85       * @param timeUnit the time unit
86       */
87      public SearchCursorImpl( SearchFuture future, long timeout, TimeUnit timeUnit )
88      {
89          if ( IS_DEBUG )
90          {
91              LOG_CURSOR.debug( "Creating SearchCursorImpl {}", this );
92          }
93  
94          this.future = future;
95          this.timeout = timeout;
96          this.timeUnit = timeUnit;
97      }
98  
99  
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 }