View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.server.core.partition.impl.btree.mavibot;
21  
22  
23  import java.io.IOException;
24  
25  import org.apache.directory.api.ldap.model.cursor.Cursor;
26  import org.apache.directory.api.ldap.model.cursor.EmptyCursor;
27  import org.apache.directory.api.ldap.model.cursor.SingletonCursor;
28  import org.apache.directory.api.ldap.model.cursor.Tuple;
29  import org.apache.directory.api.ldap.model.exception.LdapException;
30  import org.apache.directory.api.ldap.model.exception.LdapOtherException;
31  import org.apache.directory.api.ldap.model.schema.SchemaManager;
32  import org.apache.directory.mavibot.btree.BTree;
33  import org.apache.directory.mavibot.btree.BTreeFactory;
34  import org.apache.directory.mavibot.btree.RecordManager;
35  import org.apache.directory.mavibot.btree.TupleCursor;
36  import org.apache.directory.mavibot.btree.ValueCursor;
37  import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
38  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
39  import org.apache.directory.mavibot.btree.serializer.ElementSerializer;
40  import org.apache.directory.server.core.api.partition.PartitionTxn;
41  import org.apache.directory.server.core.avltree.ArrayMarshaller;
42  import org.apache.directory.server.core.avltree.ArrayTree;
43  import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
44  import org.apache.directory.server.i18n.I18n;
45  import org.apache.directory.server.xdbm.AbstractTable;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  
50  /**
51   * A Mavibot Table. It extends the default Apache DS Table, when Mavibot is the
52   * underlying database.
53   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
54   */
55  public class MavibotTable<K, V> extends AbstractTable<K, V>
56  {
57      /** the underlying B-tree */
58      private BTree<K, V> bt;
59  
60      /** The marshaller that will be used to read the values when we have more than one */
61      private ArrayMarshaller<V> arrayMarshaller;
62  
63      /** A logger for this class */
64      private static final Logger LOG = LoggerFactory.getLogger( MavibotTable.class );
65  
66      /** The used recordManager */
67      protected RecordManager recordMan;
68  
69  
70      /**
71       * Creates a new instance of MavibotTable.
72       *
73       * @param recordMan The associated RecordManager
74       * @param schemaManager The SchemaManager
75       * @param name The Table name
76       * @param keySerializer The Key serializer
77       * @param valueSerializer The Value serializer
78       * @param allowDuplicates If the table allows duplicate values
79       * @throws IOException If the instance creation failed
80       */
81      public MavibotTable( RecordManager recordMan, SchemaManager schemaManager, String name,
82          ElementSerializer<K> keySerializer, ElementSerializer<V> valueSerializer, boolean allowDuplicates )
83          throws IOException
84      {
85          this( recordMan, schemaManager, name, keySerializer, valueSerializer, allowDuplicates,
86              AbstractBTreePartition.DEFAULT_CACHE_SIZE );
87      }
88  
89  
90      /**
91       * Creates a new instance of MavibotTable.
92       *
93       * @param recordMan The associated RecordManager
94       * @param schemaManager The SchemaManager
95       * @param name The Table name
96       * @param keySerializer The Key serializer
97       * @param valueSerializer The Value serializer
98       * @param allowDuplicates If the table allows duplicate values
99       * @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 }