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.util.concurrent.TimeUnit;
025
026import org.apache.directory.api.i18n.I18n;
027import org.apache.directory.api.ldap.model.constants.Loggers;
028import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
029import org.apache.directory.api.ldap.model.cursor.CursorException;
030import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
031import org.apache.directory.api.ldap.model.cursor.SearchCursor;
032import org.apache.directory.api.ldap.model.entry.Entry;
033import org.apache.directory.api.ldap.model.exception.LdapException;
034import org.apache.directory.api.ldap.model.exception.LdapReferralException;
035import org.apache.directory.api.ldap.model.message.IntermediateResponse;
036import org.apache.directory.api.ldap.model.message.Referral;
037import org.apache.directory.api.ldap.model.message.Response;
038import org.apache.directory.api.ldap.model.message.SearchResultDone;
039import org.apache.directory.api.ldap.model.message.SearchResultEntry;
040import org.apache.directory.api.ldap.model.message.SearchResultReference;
041import org.apache.directory.ldap.client.api.future.SearchFuture;
042import org.slf4j.Logger;
043import org.slf4j.LoggerFactory;
044
045
046/**
047 * An implementation of Cursor based on the underlying SearchFuture instance.
048 * 
049 * Note: This is a forward only cursor hence the only valid operations are next(), get() and close() 
050 * 
051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
052 */
053public class SearchCursorImpl extends AbstractCursor<Response> implements SearchCursor
054{
055    /** A dedicated log for cursors */
056    private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
057
058    /** Speedup for logs */
059    private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
060
061    /** the search future */
062    private SearchFuture future;
063
064    /** wait time while polling for a SearchResponse */
065    private long timeout;
066
067    /** time units of timeout value */
068    private TimeUnit timeUnit;
069
070    /** a reference to hold the retrieved SearchResponse object from SearchFuture */
071    private Response response;
072
073    /** the done flag */
074    private boolean done;
075
076    /** a reference to hold the SearchResultDone response */
077    private SearchResultDone searchDoneResp;
078
079
080    /**
081     * Instantiates a new search cursor.
082     *
083     * @param future the future
084     * @param timeout the timeout
085     * @param timeUnit the time unit
086     */
087    public SearchCursorImpl( SearchFuture future, long timeout, TimeUnit timeUnit )
088    {
089        if ( IS_DEBUG )
090        {
091            LOG_CURSOR.debug( "Creating SearchCursorImpl {}", this );
092        }
093
094        this.future = future;
095        this.timeout = timeout;
096        this.timeUnit = timeUnit;
097    }
098
099
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}