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.partition.impl.btree.jdbm; 021 022 023import java.io.IOException; 024 025import jdbm.btree.BTree; 026import jdbm.helper.TupleBrowser; 027 028import org.apache.directory.api.ldap.model.constants.Loggers; 029import org.apache.directory.api.ldap.model.cursor.AbstractCursor; 030import org.apache.directory.api.ldap.model.cursor.CursorException; 031import org.apache.directory.api.ldap.model.cursor.InvalidCursorPositionException; 032import org.apache.directory.api.ldap.model.cursor.Tuple; 033import org.apache.directory.api.ldap.model.exception.LdapException; 034import org.apache.directory.server.i18n.I18n; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038 039/** 040 * A cursor for browsing tables with duplicates which returns the container 041 * for values rather than just the value. 042 * 043 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 044 * @param K The Key 045 * @param V The associated value 046 */ 047public class DupsContainerCursor<K, V> extends AbstractCursor<Tuple<K, DupsContainer<V>>> 048{ 049 /** A dedicated log for cursors */ 050 private static final Logger LOG_CURSOR = LoggerFactory.getLogger( Loggers.CURSOR_LOG.getName() ); 051 052 /** Speedup for logs */ 053 private static final boolean IS_DEBUG = LOG_CURSOR.isDebugEnabled(); 054 055 /** The JDBM table we are building a cursor over */ 056 private final JdbmTable<K, V> table; 057 058 /** A container to pass to the underlying JDBM to get back a tuple */ 059 private jdbm.helper.Tuple<K, V> jdbmTuple = new jdbm.helper.Tuple<>(); 060 061 private Tuple<K, DupsContainer<V>> returnedTuple = new Tuple<>(); 062 063 /** A browser over the JDBM Table */ 064 private TupleBrowser<K, V> browser; 065 066 /** Tells if we have a tuple to return */ 067 private boolean valueAvailable; 068 069 /** TODO : do we need this flag ??? */ 070 private Boolean forwardDirection; 071 072 073 /** 074 * Creates a Cursor over the tuples of a JDBM table. 075 * 076 * @param table the JDBM Table to build a Cursor over 077 */ 078 public DupsContainerCursor( JdbmTable<K, V> table ) 079 { 080 if ( IS_DEBUG ) 081 { 082 LOG_CURSOR.debug( "Creating DupsContainerCursor {}", this ); 083 } 084 085 if ( !table.isDupsEnabled() ) 086 { 087 throw new IllegalStateException( I18n.err( I18n.ERR_572 ) ); 088 } 089 090 this.table = table; 091 } 092 093 094 /** 095 * Clean the tuples we use to store the returned resut. 096 */ 097 private void clearValue() 098 { 099 returnedTuple.setKey( null ); 100 returnedTuple.setValue( null ); 101 jdbmTuple.setKey( null ); 102 jdbmTuple.setValue( null ); 103 valueAvailable = false; 104 } 105 106 107 /** 108 * {@inheritDoc} 109 */ 110 public boolean available() 111 { 112 return valueAvailable; 113 } 114 115 116 /** 117 * {@inheritDoc} 118 */ 119 @SuppressWarnings("unchecked") 120 public void beforeKey( K key ) throws CursorException 121 { 122 checkNotClosed(); 123 try 124 { 125 browser = ( ( BTree<K, V> ) table.getBTree() ).browse( key ); 126 forwardDirection = null; 127 clearValue(); 128 } 129 catch ( IOException e ) 130 { 131 throw new CursorException( e ); 132 } 133 } 134 135 136 /** 137 * {@inheritDoc} 138 */ 139 @SuppressWarnings("unchecked") 140 public void afterKey( K key ) throws CursorException 141 { 142 checkNotClosed(); 143 144 try 145 { 146 browser = ( ( BTree<K, V> ) table.getBTree() ).browse( key ); 147 forwardDirection = null; 148 149 /* 150 * While the next value is less than or equal to the element keep 151 * advancing forward to the next item. If we cannot advance any 152 * further then stop and return. If we find a value greater than 153 * the element then we stop, backup, and return so subsequent calls 154 * to getNext() will return a value greater than the element. 155 */ 156 while ( browser.getNext( jdbmTuple ) ) 157 { 158 checkNotClosed(); 159 K next = jdbmTuple.getKey(); 160 161 int nextCompared = table.getKeyComparator().compare( next, key ); 162 163 if ( nextCompared > 0 ) 164 { 165 browser.getPrevious( jdbmTuple ); 166 167 // switch in direction bug workaround: when a JDBM browser 168 // switches direction with next then previous as is occurring 169 // here then two previous moves are needed. 170 browser.getPrevious( jdbmTuple ); 171 forwardDirection = false; 172 clearValue(); 173 174 return; 175 } 176 } 177 178 clearValue(); 179 } 180 catch ( IOException e ) 181 { 182 throw new CursorException( e ); 183 } 184 } 185 186 187 /** 188 * {@inheritDoc} 189 */ 190 public void beforeValue( K key, DupsContainer<V> value ) throws Exception 191 { 192 throw new UnsupportedOperationException( I18n.err( I18n.ERR_573 ) ); 193 } 194 195 196 /** 197 * {@inheritDoc} 198 */ 199 public void afterValue( K key, DupsContainer<V> value ) throws Exception 200 { 201 throw new UnsupportedOperationException( I18n.err( I18n.ERR_573 ) ); 202 } 203 204 205 /** 206 * Positions this Cursor before the key of the supplied tuple. 207 * 208 * @param element the tuple who's key is used to position this Cursor 209 * @throws LdapException if there are failures to position the Cursor 210 * @throws CursorException if there are failures to position the Cursor 211 */ 212 public void before( Tuple<K, DupsContainer<V>> element ) throws LdapException, CursorException 213 { 214 beforeKey( element.getKey() ); 215 } 216 217 218 /** 219 * {@inheritDoc} 220 */ 221 public void after( Tuple<K, DupsContainer<V>> element ) throws CursorException 222 { 223 afterKey( element.getKey() ); 224 } 225 226 227 /** 228 * {@inheritDoc} 229 */ 230 @SuppressWarnings("unchecked") 231 public void beforeFirst() throws LdapException, CursorException 232 { 233 checkNotClosed(); 234 try 235 { 236 browser = table.getBTree().browse(); 237 forwardDirection = null; 238 clearValue(); 239 } 240 catch ( IOException e ) 241 { 242 throw new CursorException( e ); 243 } 244 } 245 246 247 /** 248 * {@inheritDoc} 249 */ 250 @SuppressWarnings("unchecked") 251 public void afterLast() throws LdapException, CursorException 252 { 253 checkNotClosed(); 254 try 255 { 256 browser = table.getBTree().browse( null ); 257 forwardDirection = null; 258 clearValue(); 259 } 260 catch ( IOException e ) 261 { 262 throw new CursorException( e ); 263 } 264 } 265 266 267 /** 268 * {@inheritDoc} 269 */ 270 public boolean first() throws LdapException, CursorException 271 { 272 beforeFirst(); 273 274 return next(); 275 } 276 277 278 /** 279 * {@inheritDoc} 280 */ 281 public boolean last() throws LdapException, CursorException 282 { 283 afterLast(); 284 285 return previous(); 286 } 287 288 289 /** 290 * {@inheritDoc} 291 */ 292 public boolean previous() throws LdapException, CursorException 293 { 294 checkNotClosed(); 295 296 if ( browser == null ) 297 { 298 afterLast(); 299 } 300 301 try 302 { 303 boolean advanceSuccess = browser.getPrevious( jdbmTuple ); 304 305 // only want to set this if the advance is a success which means we 306 // are not at front 307 if ( forwardDirection == null ) 308 { 309 if ( advanceSuccess ) 310 { 311 forwardDirection = false; 312 } 313 else 314 { 315 clearValue(); 316 317 return false; 318 } 319 } 320 else if ( forwardDirection ) 321 { 322 advanceSuccess = browser.getPrevious( jdbmTuple ); 323 forwardDirection = false; 324 } 325 326 valueAvailable = advanceSuccess; 327 328 if ( valueAvailable ) 329 { 330 returnedTuple.setKey( jdbmTuple.getKey() ); 331 returnedTuple.setValue( table.getDupsContainer( ( byte[] ) jdbmTuple.getValue() ) ); 332 } 333 else 334 { 335 clearValue(); 336 } 337 338 return valueAvailable; 339 } 340 catch ( IOException e ) 341 { 342 throw new CursorException( e ); 343 } 344 } 345 346 347 /** 348 * {@inheritDoc} 349 */ 350 public boolean next() throws LdapException, CursorException 351 { 352 checkNotClosed(); 353 354 if ( browser == null ) 355 { 356 // The tuple browser is not initialized : set it to the beginning of the cursor 357 beforeFirst(); 358 } 359 360 try 361 { 362 // Check if we can move forward and grab a tuple 363 boolean advanceSuccess = browser.getNext( jdbmTuple ); 364 365 // only want to set this if the advance is a success which means 366 // we are not at end 367 if ( forwardDirection == null ) 368 { 369 if ( advanceSuccess ) 370 { 371 forwardDirection = true; 372 } 373 else 374 { 375 clearValue(); 376 377 // No value available 378 return false; 379 } 380 } 381 382 if ( !forwardDirection ) 383 { 384 advanceSuccess = browser.getNext( jdbmTuple ); 385 forwardDirection = true; 386 } 387 388 valueAvailable = advanceSuccess; 389 390 if ( valueAvailable ) 391 { 392 // create the fetched tuple containing the key and the deserialized value 393 returnedTuple.setKey( jdbmTuple.getKey() ); 394 returnedTuple.setValue( table.getDupsContainer( ( byte[] ) jdbmTuple.getValue() ) ); 395 } 396 else 397 { 398 clearValue(); 399 } 400 401 return valueAvailable; 402 } 403 catch ( IOException e ) 404 { 405 throw new CursorException( e ); 406 } 407 } 408 409 410 /** 411 * {@inheritDoc} 412 */ 413 public Tuple<K, DupsContainer<V>> get() throws CursorException 414 { 415 checkNotClosed(); 416 417 if ( valueAvailable ) 418 { 419 return returnedTuple; 420 } 421 422 throw new InvalidCursorPositionException(); 423 } 424 425 426 /** 427 * {@inheritDoc} 428 */ 429 @Override 430 public void close() throws IOException 431 { 432 if ( IS_DEBUG ) 433 { 434 LOG_CURSOR.debug( "Closing DupsContainerCursor {}", this ); 435 } 436 437 super.close(); 438 } 439 440 441 /** 442 * {@inheritDoc} 443 */ 444 @Override 445 public void close( Exception cause ) throws IOException 446 { 447 if ( IS_DEBUG ) 448 { 449 LOG_CURSOR.debug( "Closing DupsContainerCursor {}", this ); 450 } 451 452 super.close( cause ); 453 } 454}