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