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.io.IOException;
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.CursorLdapReferralException;
031import org.apache.directory.api.ldap.model.cursor.EntryCursor;
032import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
033import org.apache.directory.api.ldap.model.cursor.SearchCursor;
034import org.apache.directory.api.ldap.model.entry.Entry;
035import org.apache.directory.api.ldap.model.exception.LdapException;
036import org.apache.directory.api.ldap.model.exception.LdapReferralException;
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.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044
045/**
046 * An implementation of Cursor based on the underlying SearchFuture instance.
047 * 
048 * Note: This is a forward only cursor hence the only valid operations are next(), get() and close() 
049 * 
050 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
051 */
052public class EntryCursorImpl extends AbstractCursor<Entry> implements EntryCursor
053{
054    /** A dedicated log for cursors */
055    private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() );
056
057    /** Speedup for logs */
058    private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled();
059
060    /** a reference to hold the retrieved SearchResponse object from SearchFuture */
061    private Response response;
062
063    /** The encapsulated search cursor */
064    private SearchCursor searchCursor;
065
066    /** The underlying messageId */
067    private int messageId;
068
069
070    /**
071     * Instantiates a new search cursor, embedding a SearchCursor.
072     *
073     * @param searchCursor the embedded SearchResponse cursor
074     */
075    public EntryCursorImpl( SearchCursor searchCursor )
076    {
077        if ( IS_DEBUG )
078        {
079            LOG_CURSOR.debug( "Creating EntryCursorImpl {}", this );
080        }
081
082        this.searchCursor = searchCursor;
083        messageId = -1;
084    }
085
086
087    /**
088     * {@inheritDoc}
089     */
090    @Override
091    public boolean next() throws LdapException, CursorException
092    {
093        if ( !searchCursor.next() )
094        {
095            return false;
096        }
097
098        try
099        {
100            do
101            {
102                response = searchCursor.get();
103
104                if ( response == null )
105                {
106                    throw new LdapException( LdapNetworkConnection.TIME_OUT_ERROR );
107                }
108
109                messageId = response.getMessageId();
110
111                if ( response instanceof SearchResultEntry )
112                {
113                    return true;
114                }
115
116                if ( response instanceof SearchResultReference )
117                {
118                    return true;
119                }
120            }
121            while ( !( response instanceof SearchResultDone ) );
122
123            return false;
124        }
125        catch ( Exception e )
126        {
127            LdapException ldapException = new LdapException( LdapNetworkConnection.NO_RESPONSE_ERROR );
128            ldapException.initCause( e );
129
130            // close the cursor
131            try
132            {
133                close( ldapException );
134            }
135            catch ( IOException ioe )
136            {
137                throw new LdapException( ioe.getMessage(), ioe );
138            }
139
140            throw ldapException;
141        }
142    }
143
144
145    /**
146     * {@inheritDoc}
147     */
148    @Override
149    public Entry get() throws CursorException
150    {
151        if ( !searchCursor.available() )
152        {
153            throw new InvalidCursorPositionException();
154        }
155
156        try
157        {
158            do
159            {
160                if ( response instanceof SearchResultEntry )
161                {
162                    return ( ( SearchResultEntry ) response ).getEntry();
163                }
164
165                if ( response instanceof SearchResultReference )
166                {
167                    throw new LdapReferralException( ( ( SearchResultReference ) response ).getReferral().getLdapUrls() );
168                }
169            }
170            while ( next() && !( response instanceof SearchResultDone ) );
171        }
172        catch ( LdapReferralException lre )
173        {
174            throw new CursorLdapReferralException( lre );
175        }
176        catch ( Exception e )
177        {
178            throw new CursorException( e );
179        }
180
181        return null;
182    }
183
184
185    /**
186     * {@inheritDoc}
187     */
188    @Override
189    public SearchResultDone getSearchResultDone()
190    {
191        return searchCursor.getSearchResultDone();
192    }
193
194
195    /**
196     * {@inheritDoc}
197     */
198    @Override
199    public boolean available()
200    {
201        return searchCursor.available();
202    }
203
204
205    /**
206     * {@inheritDoc}
207     */
208    @Override
209    public void close() throws IOException
210    {
211        if ( IS_DEBUG )
212        {
213            LOG_CURSOR.debug( "Closing EntryCursorImpl {}", this );
214        }
215
216        searchCursor.close();
217    }
218
219
220    /**
221     * {@inheritDoc}
222     */
223    @Override
224    public void close( Exception cause ) throws IOException
225    {
226        if ( IS_DEBUG )
227        {
228            LOG_CURSOR.debug( "Closing EntryCursorImpl {}", this );
229        }
230
231        searchCursor.close( cause );
232    }
233
234
235    // rest of all operations will throw UnsupportedOperationException
236
237    /**
238     * This operation is not supported in SearchCursor.
239     * {@inheritDoc}
240     */
241    @Override
242    public void after( Entry element ) throws LdapException, CursorException
243    {
244        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
245            .concat( "." ).concat( "after( Response element )" ) ) );
246    }
247
248
249    /**
250     * This operation is not supported in SearchCursor.
251     * {@inheritDoc}
252     */
253    @Override
254    public void afterLast() throws LdapException, CursorException
255    {
256        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
257            .concat( "." ).concat( "afterLast()" ) ) );
258    }
259
260
261    /**
262     * This operation is not supported in SearchCursor.
263     * {@inheritDoc}
264     */
265    @Override
266    public void before( Entry element ) throws LdapException, CursorException
267    {
268        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
269            .concat( "." ).concat( "before( Response element )" ) ) );
270    }
271
272
273    /**
274     * This operation is not supported in SearchCursor.
275     * {@inheritDoc}
276     */
277    @Override
278    public void beforeFirst() throws LdapException, CursorException
279    {
280        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
281            .concat( "." ).concat( "beforeFirst()" ) ) );
282    }
283
284
285    /**
286     * This operation is not supported in SearchCursor.
287     * {@inheritDoc}
288     */
289    @Override
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    @Override
302    public boolean last() throws LdapException, CursorException
303    {
304        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
305            .concat( "." ).concat( "last()" ) ) );
306    }
307
308
309    /**
310     * This operation is not supported in SearchCursor.
311     * {@inheritDoc}
312     */
313    @Override
314    public boolean previous() throws LdapException, CursorException
315    {
316        throw new UnsupportedOperationException( I18n.err( I18n.ERR_02014_UNSUPPORTED_OPERATION, getClass().getName()
317            .concat( "." ).concat( "previous()" ) ) );
318    }
319
320
321    /**
322     * {@inheritDoc}
323     */
324    @Override
325    public int getMessageId()
326    {
327        return messageId;
328    }
329}