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 */
020package org.apache.directory.server.core.shared;
021
022
023import java.io.File;
024import java.io.IOException;
025import java.util.List;
026import java.util.NoSuchElementException;
027
028import jdbm.RecordManager;
029import jdbm.btree.BTree;
030import jdbm.helper.Tuple;
031import jdbm.helper.TupleBrowser;
032
033import org.apache.directory.api.ldap.model.cursor.AbstractCursor;
034import org.apache.directory.api.ldap.model.cursor.CursorException;
035import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException;
036import org.apache.directory.api.ldap.model.entry.Entry;
037import org.apache.directory.api.ldap.model.exception.LdapException;
038import org.apache.directory.server.core.api.filtering.EntryFilter;
039import org.apache.directory.server.core.api.filtering.EntryFilteringCursor;
040import org.apache.directory.server.core.api.interceptor.context.SearchOperationContext;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044
045/**
046 * Cursor for sorted entries.
047 * 
048 * Note: This currently uses JDBM, but will be migrated to use Mavibot
049 *       when ready.
050 *
051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
052 */
053public class SortedEntryCursor extends AbstractCursor<Entry> implements EntryFilteringCursor
054{
055
056    private static final Logger LOG = LoggerFactory.getLogger( SortedEntryCursor.class );
057    
058    private TupleBrowser browser;
059
060    private final Tuple tuple = new Tuple();
061
062    private RecordManager recMan;
063
064    private File dataFile;
065    
066    private BTree<Entry, String> btree;
067    
068
069    public SortedEntryCursor( BTree<Entry, String> btree, RecordManager recMan, File dataFile ) throws IOException
070    {
071        this.recMan = recMan;
072        this.dataFile = dataFile;
073        this.btree = btree;
074        browser = btree.browse();
075    }
076
077
078    @Override
079    public boolean available()
080    {
081        return tuple.getKey() != null;
082    }
083
084
085    @Override
086    public void before( Entry element ) throws LdapException, CursorException
087    {
088        throw new UnsupportedOperationException();
089    }
090
091
092    @Override
093    public void after( Entry element ) throws LdapException, CursorException
094    {
095        throw new UnsupportedOperationException();
096    }
097
098
099    @Override
100    public void beforeFirst() throws LdapException, CursorException
101    {
102        try
103        {
104            clearValue();
105            browser = btree.browse();
106        }
107        catch ( IOException e )
108        {
109            throw new CursorException( e );
110        }
111    }
112
113
114    @Override
115    public void afterLast() throws LdapException, CursorException
116    {
117        try
118        {
119            clearValue();
120            browser = btree.browse( null );
121        }
122        catch ( IOException e )
123        {
124            throw new CursorException( e );
125        }
126
127    }
128
129
130    @Override
131    public boolean first() throws LdapException, CursorException
132    {
133        beforeFirst();
134        return next();
135    }
136
137
138    @Override
139    public boolean last() throws LdapException, CursorException
140    {
141        afterLast();
142        return previous();
143    }
144
145
146    @Override
147    public boolean previous() throws LdapException, CursorException
148    {
149        try
150        {
151
152            if ( browser == null )
153            {
154                browser = btree.browse( null );
155            }
156            
157            if ( browser.getPrevious( tuple ) )
158            {
159                return true;
160            }
161        }
162        catch ( IOException e )
163        {
164            throw new CursorException( e );
165        }
166        catch ( NoSuchElementException e )
167        {
168            // ignore this is due to the call wrapped.prev()
169            // instead of doing a check like if(wrapped.hasPrev())
170        }
171
172        clearValue();
173        return false;
174    }
175
176
177    @Override
178    public boolean next() throws LdapException, CursorException
179    {
180        try
181        {
182            if ( browser == null )
183            {
184                browser = btree.browse();
185            }
186            
187            if ( browser.getNext( tuple ) )
188            {
189                return true;
190            }
191        }
192        catch ( IOException e )
193        {
194            throw new CursorException( e );
195        }
196        catch ( NoSuchElementException e )
197        {
198            // ignore, this is due to the call wrapped.prev()
199            // instead of doing a check like if(wrapped.hasNext())
200        }
201
202        clearValue();
203        return false;
204    }
205
206
207    @Override
208    public Entry get() throws CursorException
209    {
210        if ( tuple.getKey() == null )
211        {
212            throw new InvalidCursorPositionException();
213        }
214
215        return ( Entry ) tuple.getKey();
216    }
217
218
219    @Override
220    public void close() throws IOException
221    {
222        deleteFile();
223        super.close();
224    }
225
226
227    @Override
228    public void close( Exception cause ) throws IOException
229    {
230        deleteFile();
231        super.close( cause );
232    }
233
234
235    @Override
236    public boolean addEntryFilter( EntryFilter filter )
237    {
238        return false;
239    }
240
241
242    @Override
243    public List<EntryFilter> getEntryFilters()
244    {
245        return null;
246    }
247
248
249    @Override
250    public SearchOperationContext getOperationContext()
251    {
252        return null;
253    }
254
255    
256    private void clearValue()
257    {
258        tuple.setKey( null );
259        tuple.setValue( null );
260    }
261
262    
263    private void deleteFile()
264    {
265        if ( recMan == null )
266        {
267            return;
268        }
269
270        try
271        {
272            recMan.close();
273            dataFile.delete();
274        }
275        catch ( IOException e )
276        {
277            LOG.warn( "Failed to delete the sorted entry data file {}", dataFile, e );
278        }
279    }
280}