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 *     https://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    /** a reference to hold the retrieved SearchResponse object from SearchFuture */
058    private Response response;
059
060    /** The encapsulated search cursor */
061    private SearchCursor searchCursor;
062
063    /** The underlying messageId */
064    private int messageId;
065
066
067    /**
068     * Instantiates a new search cursor, embedding a SearchCursor.
069     *
070     * @param searchCursor the embedded SearchResponse cursor
071     */
072    public EntryCursorImpl( SearchCursor searchCursor )
073    {
074        if ( LOG_CURSOR.isDebugEnabled() )
075        {
076            LOG_CURSOR.debug( I18n.msg( I18n.MSG_04161_CREATING_ENTRY_CURSOR, this ) );
077        }
078
079        this.searchCursor = searchCursor;
080        messageId = -1;
081    }
082
083
084    /**
085     * {@inheritDoc}
086     */
087    @Override
088    public boolean next() throws LdapException, CursorException
089    {
090        if ( !searchCursor.next() )
091        {
092            return false;
093        }
094
095        try
096        {
097            do
098            {
099                response = searchCursor.get();
100
101                if ( response == null )
102                {
103                    throw new LdapException( LdapNetworkConnection.TIME_OUT_ERROR );
104                }
105
106                messageId = response.getMessageId();
107
108                if ( response instanceof SearchResultEntry )
109                {
110                    return true;
111                }
112
113                if ( response instanceof SearchResultReference )
114                {
115                    return true;
116                }
117            }
118            while ( !( response instanceof SearchResultDone ) );
119
120            return false;
121        }
122        catch ( Exception e )
123        {
124            LdapException ldapException = new LdapException( LdapNetworkConnection.NO_RESPONSE_ERROR );
125            ldapException.initCause( e );
126
127            // close the cursor
128            try
129            {
130                close( ldapException );
131            }
132            catch ( IOException ioe )
133            {
134                throw new LdapException( ioe.getMessage(), ioe );
135            }
136
137            throw ldapException;
138        }
139    }
140
141
142    /**
143     * {@inheritDoc}
144     */
145    @Override
146    public Entry get() throws CursorException
147    {
148        if ( !searchCursor.available() )
149        {
150            throw new InvalidCursorPositionException();
151        }
152
153        try
154        {
155            do
156            {
157                if ( response instanceof SearchResultEntry )
158                {
159                    return ( ( SearchResultEntry ) response ).getEntry();
160                }
161
162                if ( response instanceof SearchResultReference )
163                {
164                    throw new LdapReferralException( ( ( SearchResultReference ) response ).getReferral().getLdapUrls() );
165                }
166            }
167            while ( next() && !( response instanceof SearchResultDone ) );
168        }
169        catch ( LdapReferralException lre )
170        {
171            throw new CursorLdapReferralException( lre );
172        }
173        catch ( Exception e )
174        {
175            throw new CursorException( e );
176        }
177
178        return null;
179    }
180
181
182    /**
183     * {@inheritDoc}
184     */
185    @Override
186    public SearchResultDone getSearchResultDone()
187    {
188        return searchCursor.getSearchResultDone();
189    }
190
191
192    /**
193     * {@inheritDoc}
194     */
195    @Override
196    public boolean available()
197    {
198        return searchCursor.available();
199    }
200
201
202    /**
203     * {@inheritDoc}
204     */
205    @Override
206    public void close() throws IOException
207    {
208        if ( LOG_CURSOR.isDebugEnabled() )
209        {
210            LOG_CURSOR.debug( I18n.msg( I18n.MSG_04162_CLOSING_ENTRY_CURSOR, this ) );
211        }
212
213        searchCursor.close();
214    }
215
216
217    /**
218     * {@inheritDoc}
219     */
220    @Override
221    public void close( Exception cause ) throws IOException
222    {
223        if ( LOG_CURSOR.isDebugEnabled() )
224        {
225            LOG_CURSOR.debug( I18n.msg( I18n.MSG_04162_CLOSING_ENTRY_CURSOR, this ) );
226        }
227
228        searchCursor.close( cause );
229    }
230
231
232    // rest of all operations will throw UnsupportedOperationException
233
234    /**
235     * This operation is not supported in SearchCursor.
236     * {@inheritDoc}
237     */
238    @Override
239    public void after( Entry element ) throws LdapException, CursorException
240    {
241        throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName()
242            .concat( "." ).concat( "after( Response element )" ) ) );
243    }
244
245
246    /**
247     * This operation is not supported in SearchCursor.
248     * {@inheritDoc}
249     */
250    @Override
251    public void afterLast() throws LdapException, CursorException
252    {
253        throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName()
254            .concat( "." ).concat( "afterLast()" ) ) );
255    }
256
257
258    /**
259     * This operation is not supported in SearchCursor.
260     * {@inheritDoc}
261     */
262    @Override
263    public void before( Entry element ) throws LdapException, CursorException
264    {
265        throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName()
266            .concat( "." ).concat( "before( Response element )" ) ) );
267    }
268
269
270    /**
271     * This operation is not supported in SearchCursor.
272     * {@inheritDoc}
273     */
274    @Override
275    public void beforeFirst() throws LdapException, CursorException
276    {
277        throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName()
278            .concat( "." ).concat( "beforeFirst()" ) ) );
279    }
280
281
282    /**
283     * This operation is not supported in SearchCursor.
284     * {@inheritDoc}
285     */
286    @Override
287    public boolean first() throws LdapException, CursorException
288    {
289        throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName()
290            .concat( "." ).concat( "first()" ) ) );
291    }
292
293
294    /**
295     * This operation is not supported in SearchCursor.
296     * {@inheritDoc}
297     */
298    @Override
299    public boolean last() throws LdapException, CursorException
300    {
301        throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName()
302            .concat( "." ).concat( "last()" ) ) );
303    }
304
305
306    /**
307     * This operation is not supported in SearchCursor.
308     * {@inheritDoc}
309     */
310    @Override
311    public boolean previous() throws LdapException, CursorException
312    {
313        throw new UnsupportedOperationException( I18n.err( I18n.ERR_13102_UNSUPPORTED_OPERATION, getClass().getName()
314            .concat( "." ).concat( "previous()" ) ) );
315    }
316
317
318    /**
319     * {@inheritDoc}
320     */
321    @Override
322    public int getMessageId()
323    {
324        return messageId;
325    }
326}