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.mavibot; 021 022 023import java.io.IOException; 024 025import org.apache.directory.api.ldap.model.cursor.Cursor; 026import org.apache.directory.api.ldap.model.cursor.EmptyCursor; 027import org.apache.directory.api.ldap.model.cursor.SingletonCursor; 028import org.apache.directory.api.ldap.model.cursor.Tuple; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.apache.directory.api.ldap.model.exception.LdapOtherException; 031import org.apache.directory.api.ldap.model.schema.SchemaManager; 032import org.apache.directory.mavibot.btree.BTree; 033import org.apache.directory.mavibot.btree.BTreeFactory; 034import org.apache.directory.mavibot.btree.RecordManager; 035import org.apache.directory.mavibot.btree.TupleCursor; 036import org.apache.directory.mavibot.btree.ValueCursor; 037import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException; 038import org.apache.directory.mavibot.btree.exception.KeyNotFoundException; 039import org.apache.directory.mavibot.btree.serializer.ElementSerializer; 040import org.apache.directory.server.core.api.partition.PartitionTxn; 041import org.apache.directory.server.core.avltree.ArrayMarshaller; 042import org.apache.directory.server.core.avltree.ArrayTree; 043import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition; 044import org.apache.directory.server.i18n.I18n; 045import org.apache.directory.server.xdbm.AbstractTable; 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049 050/** 051 * A Mavibot Table. It extends the default Apache DS Table, when Mavibot is the 052 * underlying database. 053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 054 */ 055public class MavibotTable<K, V> extends AbstractTable<K, V> 056{ 057 /** the underlying B-tree */ 058 private BTree<K, V> bt; 059 060 /** The marshaller that will be used to read the values when we have more than one */ 061 private ArrayMarshaller<V> arrayMarshaller; 062 063 /** A logger for this class */ 064 private static final Logger LOG = LoggerFactory.getLogger( MavibotTable.class ); 065 066 /** The used recordManager */ 067 protected RecordManager recordMan; 068 069 070 /** 071 * Creates a new instance of MavibotTable. 072 * 073 * @param recordMan The associated RecordManager 074 * @param schemaManager The SchemaManager 075 * @param name The Table name 076 * @param keySerializer The Key serializer 077 * @param valueSerializer The Value serializer 078 * @param allowDuplicates If the table allows duplicate values 079 * @throws IOException If the instance creation failed 080 */ 081 public MavibotTable( RecordManager recordMan, SchemaManager schemaManager, String name, 082 ElementSerializer<K> keySerializer, ElementSerializer<V> valueSerializer, boolean allowDuplicates ) 083 throws IOException 084 { 085 this( recordMan, schemaManager, name, keySerializer, valueSerializer, allowDuplicates, 086 AbstractBTreePartition.DEFAULT_CACHE_SIZE ); 087 } 088 089 090 /** 091 * Creates a new instance of MavibotTable. 092 * 093 * @param recordMan The associated RecordManager 094 * @param schemaManager The SchemaManager 095 * @param name The Table name 096 * @param keySerializer The Key serializer 097 * @param valueSerializer The Value serializer 098 * @param allowDuplicates If the table allows duplicate values 099 * @param cacheSize The cache size to use 100 * @throws IOException If the instance creation failed 101 */ 102 public MavibotTable( RecordManager recordMan, SchemaManager schemaManager, String name, 103 ElementSerializer<K> keySerializer, ElementSerializer<V> valueSerializer, boolean allowDuplicates, int cacheSize ) 104 throws IOException 105 { 106 super( schemaManager, name, keySerializer.getComparator(), valueSerializer.getComparator() ); 107 this.recordMan = recordMan; 108 109 bt = recordMan.getManagedTree( name ); 110 111 if ( bt == null ) 112 { 113 bt = BTreeFactory.createPersistedBTree( name, keySerializer, valueSerializer, allowDuplicates, cacheSize ); 114 115 try 116 { 117 recordMan.manage( bt ); 118 } 119 catch ( BTreeAlreadyManagedException e ) 120 { 121 // should never happen 122 throw new RuntimeException( e ); 123 } 124 } 125 else 126 { 127 // it is important to set the serializers cause serializers will contain default 128 // comparators when loaded from disk and we need schema aware comparators in certain indices 129 bt.setKeySerializer( keySerializer ); 130 bt.setValueSerializer( valueSerializer ); 131 } 132 133 this.allowsDuplicates = allowDuplicates; 134 arrayMarshaller = new ArrayMarshaller<>( valueComparator ); 135 136 // Initialize the count 137 count = bt.getNbElems(); 138 } 139 140 141 /** 142 * {@inheritDoc} 143 */ 144 @Override 145 public boolean has( PartitionTxn partitionTxn, K key ) throws LdapException 146 { 147 try 148 { 149 return bt.hasKey( key ); 150 } 151 catch ( IOException ioe ) 152 { 153 throw new LdapException( ioe ); 154 } 155 catch ( KeyNotFoundException knfe ) 156 { 157 throw new LdapException( knfe ); 158 } 159 } 160 161 162 /** 163 * {@inheritDoc} 164 */ 165 @Override 166 public boolean has( PartitionTxn transaction, K key, V value ) throws LdapException 167 { 168 try 169 { 170 return bt.contains( key, value ); 171 } 172 catch ( IOException e ) 173 { 174 throw new LdapException( e ); 175 } 176 } 177 178 179 /** 180 * {@inheritDoc} 181 */ 182 @Override 183 public boolean hasGreaterOrEqual( PartitionTxn transaction, K key ) throws LdapException 184 { 185 TupleCursor<K, V> cursor = null; 186 187 try 188 { 189 cursor = bt.browseFrom( key ); 190 191 return cursor.hasNext(); 192 } 193 catch ( IOException ioe ) 194 { 195 throw new LdapOtherException( ioe.getMessage() ); 196 } 197 finally 198 { 199 if ( cursor != null ) 200 { 201 cursor.close(); 202 } 203 } 204 } 205 206 207 /** 208 * {@inheritDoc} 209 */ 210 @Override 211 public boolean hasLessOrEqual( PartitionTxn transaction, K key ) throws LdapException 212 { 213 TupleCursor<K, V> cursor = null; 214 215 try 216 { 217 cursor = bt.browseFrom( key ); 218 219 org.apache.directory.mavibot.btree.Tuple<K, V> tuple = null; 220 221 if ( cursor.hasNext() ) 222 { 223 tuple = cursor.next(); 224 } 225 226 // Test for equality first since it satisfies both greater/less than 227 if ( null != tuple && keyComparator.compare( tuple.getKey(), key ) == 0 ) 228 { 229 return true; 230 } 231 232 if ( null == tuple ) 233 { 234 return count > 0; 235 } 236 else 237 { 238 if ( cursor.hasPrev() ) 239 { 240 return true; 241 } 242 } 243 244 return false; 245 } 246 catch ( Exception e ) 247 { 248 throw new LdapException( e ); 249 } 250 finally 251 { 252 if ( cursor != null ) 253 { 254 cursor.close(); 255 } 256 } 257 } 258 259 260 /** 261 * {@inheritDoc} 262 */ 263 @Override 264 public boolean hasGreaterOrEqual( PartitionTxn transaction, K key, V val ) throws LdapException 265 { 266 if ( key == null ) 267 { 268 return false; 269 } 270 271 if ( !allowsDuplicates ) 272 { 273 throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) ); 274 } 275 276 ValueCursor<V> valueCursor = null; 277 278 try 279 { 280 if ( !bt.hasKey( key ) ) 281 { 282 return false; 283 } 284 285 valueCursor = bt.getValues( key ); 286 287 int equal = bt.getValueSerializer().compare( val, valueCursor.next() ); 288 289 return ( equal >= 0 ); 290 } 291 catch ( KeyNotFoundException | IOException e ) 292 { 293 throw new LdapException( e.getMessage() ); 294 } 295 finally 296 { 297 if ( valueCursor != null ) 298 { 299 valueCursor.close(); 300 } 301 } 302 } 303 304 305 /** 306 * {@inheritDoc} 307 */ 308 @Override 309 public boolean hasLessOrEqual( PartitionTxn partitionTxn, K key, V val ) throws LdapException 310 { 311 if ( key == null ) 312 { 313 return false; 314 } 315 316 if ( !allowsDuplicates ) 317 { 318 throw new UnsupportedOperationException( I18n.err( I18n.ERR_593 ) ); 319 } 320 321 try 322 { 323 if ( !bt.hasKey( key ) ) 324 { 325 return false; 326 } 327 328 ValueCursor<V> dupHolder = bt.getValues( key ); 329 330 return dupHolder.hasNext(); 331 } 332 catch ( KeyNotFoundException knfe ) 333 { 334 throw new LdapOtherException( knfe.getMessage(), knfe ); 335 } 336 catch ( IOException ioe ) 337 { 338 throw new LdapOtherException( ioe.getMessage(), ioe ); 339 } 340 } 341 342 343 /** 344 * {@inheritDoc} 345 */ 346 @Override 347 public V get( PartitionTxn transaction, K key ) throws LdapException 348 { 349 if ( key == null ) 350 { 351 return null; 352 } 353 354 try 355 { 356 return bt.get( key ); 357 } 358 catch ( KeyNotFoundException knfe ) 359 { 360 return null; 361 } 362 catch ( Exception e ) 363 { 364 throw new LdapException( e ); 365 } 366 } 367 368 369 /** 370 * {@inheritDoc} 371 */ 372 @Override 373 public void put( PartitionTxn partitionTxn, K key, V value ) throws LdapException 374 { 375 try 376 { 377 if ( ( value == null ) || ( key == null ) ) 378 { 379 throw new IllegalArgumentException( I18n.err( I18n.ERR_594 ) ); 380 } 381 382 V existingVal = bt.insert( key, value ); 383 384 if ( existingVal == null ) 385 { 386 count++; 387 } 388 } 389 catch ( IOException ioe ) 390 { 391 LOG.error( I18n.err( I18n.ERR_131, key, name ), ioe ); 392 throw new LdapOtherException( ioe.getMessage(), ioe ); 393 } 394 } 395 396 397 /** 398 * {@inheritDoc} 399 */ 400 @Override 401 public void remove( PartitionTxn partitionTxn, K key ) throws LdapException 402 { 403 try 404 { 405 if ( key == null ) 406 { 407 return; 408 } 409 410 // Get the associated valueHolder 411 if ( bt.isAllowDuplicates() ) 412 { 413 ValueCursor<V> valueCursor = bt.getValues( key ); 414 int size = valueCursor.size(); 415 valueCursor.close(); 416 org.apache.directory.mavibot.btree.Tuple<K, V> returned = bt.delete( key ); 417 418 if ( null == returned ) 419 { 420 return; 421 } 422 423 count -= size; 424 } 425 else 426 { 427 org.apache.directory.mavibot.btree.Tuple<K, V> returned = bt.delete( key ); 428 429 if ( null == returned ) 430 { 431 return; 432 } 433 434 count--; 435 } 436 } 437 catch ( IOException | KeyNotFoundException e ) 438 { 439 LOG.error( I18n.err( I18n.ERR_133, key, name ), e ); 440 441 throw new LdapOtherException( e.getMessage(), e ); 442 } 443 } 444 445 446 /** 447 * {@inheritDoc} 448 */ 449 @Override 450 public void remove( PartitionTxn partitionTxn, K key, V value ) throws LdapException 451 { 452 try 453 { 454 if ( key == null ) 455 { 456 return; 457 } 458 459 org.apache.directory.mavibot.btree.Tuple<K, V> tuple = bt.delete( key, value ); 460 461 // We decrement the counter only when the key was found 462 if ( tuple != null ) 463 { 464 count--; 465 } 466 } 467 catch ( Exception e ) 468 { 469 LOG.error( I18n.err( I18n.ERR_132, key, value, name ), e ); 470 } 471 } 472 473 474 /** 475 * {@inheritDoc} 476 */ 477 @Override 478 public Cursor<Tuple<K, V>> cursor() 479 { 480 return new MavibotCursor<>( this ); 481 } 482 483 484 /** 485 * {@inheritDoc} 486 */ 487 @Override 488 public Cursor<Tuple<K, V>> cursor( PartitionTxn partitionTxn, K key ) throws LdapException 489 { 490 if ( key == null ) 491 { 492 return new EmptyCursor<>(); 493 } 494 495 try 496 { 497 if ( !allowsDuplicates ) 498 { 499 V val = bt.get( key ); 500 501 return new SingletonCursor<>( new Tuple<K, V>( key, val ) ); 502 } 503 else 504 { 505 ValueCursor<V> dupHolder = bt.getValues( key ); 506 507 return new KeyTupleValueCursor<>( dupHolder, key ); 508 } 509 } 510 catch ( KeyNotFoundException knfe ) 511 { 512 return new EmptyCursor<>(); 513 } 514 catch ( Exception e ) 515 { 516 throw new LdapException( e ); 517 } 518 } 519 520 521 /** 522 * {@inheritDoc} 523 */ 524 @Override 525 public Cursor<V> valueCursor( PartitionTxn transaction, K key ) throws LdapException 526 { 527 if ( key == null ) 528 { 529 return new EmptyCursor<>(); 530 } 531 532 try 533 { 534 if ( !allowsDuplicates ) 535 { 536 V val = bt.get( key ); 537 538 return new SingletonCursor<>( val ); 539 } 540 else 541 { 542 ValueCursor<V> dupCursor = bt.getValues( key ); 543 544 return new ValueTreeCursor<>( dupCursor ); 545 } 546 } 547 catch ( KeyNotFoundException knfe ) 548 { 549 return new EmptyCursor<>(); 550 } 551 catch ( Exception e ) 552 { 553 throw new LdapException( e ); 554 } 555 } 556 557 558 /** 559 * {@inheritDoc} 560 */ 561 @Override 562 public synchronized void close( PartitionTxn transaction ) throws LdapException 563 { 564 try 565 { 566 sync(); 567 } 568 catch ( IOException ioe ) 569 { 570 throw new LdapOtherException( ioe.getMessage() ); 571 } 572 } 573 574 575 /** 576 * {@inheritDoc} 577 */ 578 @Override 579 public long count( PartitionTxn transaction, K key ) throws LdapException 580 { 581 if ( key == null ) 582 { 583 return 0; 584 } 585 586 try 587 { 588 if ( bt.isAllowDuplicates() ) 589 { 590 ValueCursor<V> dupHolder = bt.getValues( key ); 591 int size = dupHolder.size(); 592 dupHolder.close(); 593 594 return size; 595 } 596 else 597 { 598 if ( bt.hasKey( key ) ) 599 { 600 return 1; 601 } 602 else 603 { 604 return 0; 605 } 606 } 607 } 608 catch ( KeyNotFoundException knfe ) 609 { 610 // No key 611 return 0; 612 } 613 catch ( IOException ioe ) 614 { 615 throw new LdapOtherException( ioe.getMessage() ); 616 } 617 } 618 619 620 /** 621 * {@inheritDoc} 622 */ 623 public ArrayTree<V> getDupsContainer( byte[] serialized ) throws IOException 624 { 625 if ( serialized == null ) 626 { 627 return new ArrayTree<>( valueComparator ); 628 } 629 630 return arrayMarshaller.deserialize( serialized ); 631 } 632 633 634 /** 635 * @return the underlying B-tree 636 */ 637 protected BTree<K, V> getBTree() 638 { 639 return bt; 640 } 641 642 643 /** 644 * Synchronizes the buffers with disk. 645 * 646 * @throws IOException if errors are encountered on the flush 647 */ 648 public synchronized void sync() throws IOException 649 { 650 } 651 652 653 /** 654 * @see Object#toString() 655 */ 656 @Override 657 public String toString() 658 { 659 StringBuilder sb = new StringBuilder(); 660 661 sb.append( "Mavibot table :\n" ).append( super.toString() ); 662 663 return sb.toString(); 664 } 665}