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.mavibot.btree;
21  
22  
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertNotNull;
26  import static org.junit.Assert.assertNull;
27  import static org.junit.Assert.assertTrue;
28  import static org.junit.Assert.fail;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.util.NoSuchElementException;
33  import java.util.UUID;
34  
35  import org.apache.commons.io.FileUtils;
36  import org.apache.directory.mavibot.btree.exception.BTreeAlreadyManagedException;
37  import org.apache.directory.mavibot.btree.exception.DuplicateValueNotAllowedException;
38  import org.apache.directory.mavibot.btree.exception.KeyNotFoundException;
39  import org.apache.directory.mavibot.btree.serializer.IntSerializer;
40  import org.apache.directory.mavibot.btree.serializer.LongSerializer;
41  import org.apache.directory.mavibot.btree.serializer.StringSerializer;
42  import org.junit.After;
43  import org.junit.Before;
44  import org.junit.Rule;
45  import org.junit.Test;
46  import org.junit.rules.TemporaryFolder;
47  
48  
49  /**
50   * TODO BTreeDuplicateKeyTest.
51   *
52   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
53   */
54  public class PersistedBTreeDuplicateKeyTest
55  {
56      private BTree<Long, String> btree = null;
57  
58      private RecordManager recordManager1 = null;
59  
60      @Rule
61      public TemporaryFolder tempFolder = new TemporaryFolder();
62  
63      private File dataDir = null;
64  
65  
66      @Before
67      public void createBTree() throws IOException
68      {
69          dataDir = tempFolder.newFolder( UUID.randomUUID().toString() );
70  
71          openRecordManagerAndBtree();
72  
73          try
74          {
75              // Create a new BTree
76              btree = recordManager1.addBTree( "test", LongSerializer.INSTANCE, StringSerializer.INSTANCE,
77                  BTree.ALLOW_DUPLICATES );
78          }
79          catch ( Exception e )
80          {
81              throw new RuntimeException( e );
82          }
83      }
84  
85  
86      @After
87      public void cleanup() throws IOException
88      {
89          dataDir = new File( System.getProperty( "java.io.tmpdir" ) + "/recordman" );
90  
91          btree.close();
92  
93          if ( dataDir.exists() )
94          {
95              FileUtils.deleteDirectory( dataDir );
96          }
97          
98          recordManager1.close();
99          assertTrue( recordManager1.isContextOk() );
100     }
101 
102 
103     private void openRecordManagerAndBtree()
104     {
105         try
106         {
107             if ( recordManager1 != null )
108             {
109                 recordManager1.close();
110             }
111 
112             // Now, try to reload the file back
113             recordManager1 = new RecordManager( dataDir.getAbsolutePath() );
114 
115             // load the last created btree
116             if ( btree != null )
117             {
118                 btree = recordManager1.getManagedTree( btree.getName() );
119             }
120         }
121         catch ( Exception e )
122         {
123             throw new RuntimeException( e );
124         }
125     }
126 
127 
128     @Test
129     public void testInsertNullValue() throws IOException, KeyNotFoundException
130     {
131         btree.insert( 1L, null );
132 
133         TupleCursor<Long, String> cursor = btree.browse();
134         assertTrue( cursor.hasNext() );
135 
136         Tuple<Long, String> t = cursor.next();
137 
138         assertEquals( Long.valueOf( 1 ), t.getKey() );
139         assertEquals( null, t.getValue() );
140 
141         cursor.close();
142 
143         btree.close();
144     }
145 
146 
147     @Test
148     public void testBrowseEmptyTree() throws IOException, KeyNotFoundException, BTreeAlreadyManagedException
149     {
150         IntSerializer serializer = IntSerializer.INSTANCE;
151 
152         BTree<Integer, Integer> btree = BTreeFactory.createPersistedBTree( "master", serializer, serializer );
153         
154         // Inject the newly created BTree into teh recordManager
155         recordManager1.manage( btree );
156 
157         TupleCursor<Integer, Integer> cursor = btree.browse();
158         assertFalse( cursor.hasNext() );
159         assertFalse( cursor.hasPrev() );
160 
161         try
162         {
163             cursor.next();
164             fail( "Should not reach here" );
165         }
166         catch ( NoSuchElementException e )
167         {
168             assertTrue( true );
169         }
170 
171         try
172         {
173             cursor.prev();
174             fail( "Should not reach here" );
175         }
176         catch ( NoSuchElementException e )
177         {
178             assertTrue( true );
179         }
180 
181         cursor.close();
182         btree.close();
183     }
184 
185 
186     @Test
187     public void testDuplicateKey() throws IOException, KeyNotFoundException
188     {
189         btree.insert( 1L, "1" );
190         btree.insert( 1L, "2" );
191 
192         TupleCursor<Long, String> cursor = btree.browse();
193         assertTrue( cursor.hasNext() );
194 
195         Tuple<Long, String> t = cursor.next();
196 
197         assertEquals( Long.valueOf( 1 ), t.getKey() );
198         assertEquals( "1", t.getValue() );
199 
200         assertTrue( cursor.hasNext() );
201 
202         t = cursor.next();
203 
204         assertEquals( Long.valueOf( 1 ), t.getKey() );
205         assertEquals( "2", t.getValue() );
206 
207         assertFalse( cursor.hasNext() );
208 
209         // test backward move
210         assertTrue( cursor.hasPrev() );
211 
212         t = cursor.prev();
213 
214         assertEquals( Long.valueOf( 1 ), t.getKey() );
215         assertEquals( "1", t.getValue() );
216 
217         assertFalse( cursor.hasPrev() );
218 
219         // again forward
220         assertTrue( cursor.hasNext() );
221 
222         t = cursor.next();
223 
224         assertEquals( Long.valueOf( 1 ), t.getKey() );
225         assertEquals( "2", t.getValue() );
226 
227         assertFalse( cursor.hasNext() );
228 
229         cursor.close();
230         btree.close();
231     }
232 
233 
234     @Test
235     public void testGetDuplicateKey() throws Exception
236     {
237         String retVal = btree.insert( 1L, "1" );
238         assertNull( retVal );
239 
240         retVal = btree.insert( 1L, "2" );
241         assertNull( retVal );
242 
243         // check the return value when an existing value is added again
244         retVal = btree.insert( 1L, "2" );
245         assertEquals( "2", retVal );
246 
247         assertEquals( "1", btree.get( 1L ) );
248         assertTrue( btree.contains( 1L, "1" ) );
249         assertTrue( btree.contains( 1L, "2" ) );
250 
251         assertFalse( btree.contains( 1L, "0" ) );
252         assertFalse( btree.contains( 0L, "1" ) );
253         assertFalse( btree.contains( 0L, "0" ) );
254         assertFalse( btree.contains( null, "0" ) );
255         assertFalse( btree.contains( 0L, null ) );
256         assertFalse( btree.contains( null, null ) );
257         btree.close();
258     }
259 
260 
261     @Test
262     public void testRemoveDuplicateKey() throws Exception
263     {
264         btree.insert( 1L, "1" );
265         btree.insert( 1L, "2" );
266 
267         assertEquals( 2, btree.getNbElems() );
268 
269         Tuple<Long, String> t = btree.delete( 1L, "1" );
270         assertEquals( Long.valueOf( 1 ), t.getKey() );
271         assertEquals( "1", t.getValue() );
272 
273         assertEquals( 1l, btree.getNbElems() );
274 
275         t = btree.delete( 1L, "2" );
276         assertEquals( Long.valueOf( 1 ), t.getKey() );
277         assertEquals( "2", t.getValue() );
278 
279         assertEquals( 0l, btree.getNbElems() );
280 
281         t = btree.delete( 1L, "2" );
282         assertNull( t );
283         btree.close();
284     }
285 
286 
287     @Test
288     public void testFullPage() throws Exception
289     {
290         int i = 7;
291         for ( char ch = 'a'; ch <= 'z'; ch++ )
292         {
293             for ( int k = 0; k < i; k++ )
294             {
295                 String val = ch + Integer.toString( k );
296                 btree.insert( Long.valueOf( ch ), val );
297             }
298         }
299 
300         TupleCursor<Long, String> cursor = btree.browse();
301 
302         char ch = 'a';
303         int k = 0;
304 
305         while ( cursor.hasNext() )
306         {
307             Tuple<Long, String> t = cursor.next();
308             assertEquals( Long.valueOf( ch ), t.getKey() );
309             k++;
310 
311             if ( ( k % i ) == 0 )
312             {
313                 ch++;
314             }
315         }
316 
317         assertEquals( ( 'z' + 1 ), ch );
318 
319         ch = 'z';
320         cursor.afterLast();
321 
322         while ( cursor.hasPrev() )
323         {
324             Tuple<Long, String> t = cursor.prev();
325             assertEquals( Long.valueOf( ch ), t.getKey() );
326             k--;
327 
328             if ( ( k % i ) == 0 )
329             {
330                 ch--;
331             }
332         }
333 
334         assertEquals( ( 'a' - 1 ), ch );
335         cursor.close();
336     }
337 
338 
339     @Test
340     public void testMoveFirst() throws Exception
341     {
342         for ( char ch = 'a'; ch <= 'z'; ch++ )
343         {
344             String val = Character.toString( ch );
345             btree.insert( Long.valueOf( ch ), val );
346         }
347 
348         assertEquals( 26, btree.getNbElems() );
349 
350         // add one more value for 'a'
351         btree.insert( Long.valueOf( 'a' ), "val" );
352 
353         assertEquals( 27, btree.getNbElems() );
354 
355         // Start from c : we should have only 24 values
356         TupleCursor<Long, String> cursor = btree.browseFrom( Long.valueOf( 'c' ) );
357 
358         int i = 0;
359 
360         while ( cursor.hasNext() )
361         {
362             Tuple<Long, String> tuple = cursor.next();
363             assertNotNull( tuple );
364             i++;
365         }
366 
367         assertEquals( 24, i );
368 
369         // now move the cursor first
370         cursor.beforeFirst();
371         assertTrue( cursor.hasNext() );
372         Tuple<Long, String> tuple = cursor.next();
373 
374         // We should be on the first position
375         assertEquals( Long.valueOf( 'a' ), tuple.getKey() );
376 
377         // Count the number of element after the first one, we should have 26 only
378         i = 0;
379 
380         while ( cursor.hasNext() )
381         {
382             tuple = cursor.next();
383             assertNotNull( tuple );
384             i++;
385         }
386 
387         assertEquals( 26, i );
388 
389         cursor.close();
390 
391         // Rebrowse
392         cursor = btree.browse();
393 
394         i = 0;
395 
396         while ( cursor.hasNext() )
397         {
398             assertNotNull( cursor.next() );
399             i++;
400         }
401 
402         // again, we should see 27 elements
403         assertEquals( 27, i );
404 
405         // now move the cursor first, but move forward the keys
406         cursor.beforeFirst();
407         assertTrue( cursor.hasNextKey() );
408         assertEquals( Long.valueOf( 'a' ), cursor.nextKey().getKey() );
409 
410         i = 0;
411 
412         while ( cursor.hasNextKey() )
413         {
414             tuple = cursor.nextKey();
415             long key = tuple.getKey();
416             assertNotNull( key );
417             i++;
418         }
419 
420         // We should have 25 keys only, as we just moved forward the first one
421         assertEquals( 25, i );
422     }
423 
424 
425     @Test
426     public void testMoveLast() throws Exception
427     {
428         for ( char ch = 'a'; ch <= 'z'; ch++ )
429         {
430             String val = Character.toString( ch );
431             btree.insert( Long.valueOf( ch ), val );
432         }
433 
434         assertEquals( 26, btree.getNbElems() );
435 
436         // add one more value for 'z'
437         btree.insert( Long.valueOf( 'z' ), "val" );
438 
439         assertEquals( 27, btree.getNbElems() );
440 
441         // Start from x : we should have only 23 values
442         TupleCursor<Long, String> cursor = btree.browseFrom( Long.valueOf( 'x' ) );
443 
444         int i = 0;
445 
446         while ( cursor.hasPrev() )
447         {
448             Tuple<Long, String> tuple = cursor.prev();
449             assertNotNull( tuple );
450             i++;
451         }
452 
453         assertEquals( 23, i );
454 
455         // now move the cursor to the last element
456         cursor.afterLast();
457         assertTrue( cursor.hasPrev() );
458         Tuple<Long, String> tuple = cursor.prev();
459 
460         // We should be on the last position
461         assertEquals( Long.valueOf( 'z' ), tuple.getKey() );
462 
463         // Count the number of element before the last one, we should have 26
464         i = 0;
465 
466         while ( cursor.hasPrev() )
467         {
468             tuple = cursor.prev();
469             assertNotNull( tuple );
470             i++;
471         }
472 
473         assertEquals( 26, i );
474 
475         cursor.close();
476 
477         // Rebrowse
478         cursor = btree.browse();
479         cursor.afterLast();
480 
481         i = 0;
482 
483         while ( cursor.hasPrev() )
484         {
485             assertNotNull( cursor.prev() );
486             i++;
487         }
488 
489         // again, we should see 27 elements
490         assertEquals( 27, i );
491 
492         // now move the cursor first, but move backward the keys
493         cursor.afterLast();
494         assertTrue( cursor.hasPrevKey() );
495         assertEquals( Long.valueOf( 'z' ), cursor.prevKey().getKey() );
496 
497         i = 0;
498 
499         while ( cursor.hasPrevKey() )
500         {
501             tuple = cursor.prevKey();
502             long key = tuple.getKey();
503             assertNotNull( key );
504             i++;
505         }
506 
507         // We should have 25 keys only, as we just moved forward the first one
508         assertEquals( 25, i );
509     }
510 
511 
512     @Test(expected = NoSuchElementException.class)
513     public void testMoveLast2() throws Exception
514     {
515         for ( char ch = 'a'; ch <= 'z'; ch++ )
516         {
517             btree.insert( Long.valueOf( ch ), UUID.randomUUID().toString() );
518         }
519 
520         btree.insert( Long.valueOf( 'z' ), UUID.randomUUID().toString() );
521 
522         TupleCursor<Long, String> cursor = btree.browseFrom( Long.valueOf( 'c' ) );
523         cursor.afterLast();
524 
525         assertFalse( cursor.hasNext() );
526         assertTrue( cursor.hasPrev() );
527         assertEquals( Long.valueOf( 'z' ), cursor.prev().getKey() );
528         // the key, 'z', has two values
529         assertEquals( Long.valueOf( 'z' ), cursor.prev().getKey() );
530         assertEquals( Long.valueOf( 'y' ), cursor.prev().getKey() );
531 
532         cursor.beforeFirst();
533         assertEquals( Long.valueOf( 'a' ), cursor.next().getKey() );
534 
535         cursor.afterLast();
536         assertFalse( cursor.hasNext() );
537         // make sure it throws NoSuchElementException
538         cursor.next();
539     }
540 
541 
542     @Test(expected = NoSuchElementException.class)
543     public void testNextPrevKey() throws Exception
544     {
545         int i = 7;
546 
547         // Insert keys from a to z with 7 values for each key
548         for ( char ch = 'a'; ch <= 'z'; ch++ )
549         {
550             for ( int k = 0; k < i; k++ )
551             {
552                 btree.insert( Long.valueOf( ch ), String.valueOf( k ) );
553             }
554         }
555 
556         TupleCursor<Long, String> cursor = btree.browse();
557 
558         assertTrue( cursor.hasNext() );
559         assertFalse( cursor.hasPrev() );
560 
561         for ( int k = 0; k < 2; k++ )
562         {
563             assertEquals( Long.valueOf( 'a' ), cursor.next().getKey() );
564         }
565 
566         assertEquals( Long.valueOf( 'a' ), cursor.next().getKey() );
567 
568         Tuple<Long, String> tuple = cursor.nextKey();
569 
570         assertEquals( Long.valueOf( 'b' ), tuple.getKey() );
571 
572         for ( char ch = 'b'; ch < 'z'; ch++ )
573         {
574             assertEquals( Long.valueOf( ch ), cursor.next().getKey() );
575             tuple = cursor.nextKey();
576             char t = ch;
577             assertEquals( Long.valueOf( ++t ), tuple.getKey() );
578         }
579 
580         for ( int k = 0; k < i; k++ )
581         {
582             assertEquals( Long.valueOf( 'z' ), cursor.next().getKey() );
583         }
584 
585         assertFalse( cursor.hasNextKey() );
586         assertTrue( cursor.hasPrevKey() );
587         tuple = cursor.prev();
588         assertEquals( Long.valueOf( 'z' ), tuple.getKey() );
589         assertEquals( "6", tuple.getValue() );
590 
591         for ( char ch = 'z'; ch > 'a'; ch-- )
592         {
593             char t = ch;
594             t--;
595 
596             assertEquals( Long.valueOf( ch ), cursor.prev().getKey() );
597 
598             tuple = cursor.prevKey();
599 
600             assertEquals( Long.valueOf( t ), tuple.getKey() );
601         }
602 
603         for ( int k = 5; k >= 0; k-- )
604         {
605             tuple = cursor.prev();
606             assertEquals( Long.valueOf( 'a' ), tuple.getKey() );
607             assertEquals( String.valueOf( k ), tuple.getValue() );
608         }
609 
610         assertTrue( cursor.hasNext() );
611         assertFalse( cursor.hasPrev() );
612         tuple = cursor.next();
613         assertEquals( Long.valueOf( 'a' ), tuple.getKey() );
614         assertEquals( "0", tuple.getValue() );
615 
616         cursor.close();
617 
618         cursor = btree.browseFrom( Long.valueOf( 'y' ) );
619         tuple = cursor.prevKey();
620         assertNotNull( tuple );
621         assertEquals( Long.valueOf( 'y' ), tuple.getKey() );
622         assertEquals( "6", tuple.getValue() );
623         cursor.close();
624 
625         cursor = btree.browse();
626         cursor.beforeFirst();
627         assertFalse( cursor.hasPrev() );
628         // make sure it throws NoSuchElementException
629         cursor.prev();
630     }
631 
632 
633     /**
634      * Test for moving between two leaves. When moveToNextNonDuplicateKey is called
635      * and cursor is on the last element of the current leaf.
636      *
637      * @throws Exception
638      */
639     @Test
640     public void testMoveToNextAndPrevWithPageBoundaries() throws Exception
641     {
642         int i = 32;
643         for ( int k = 0; k < i; k++ )
644         {
645             btree.insert( ( long ) k, Long.toString( k ) );
646         }
647 
648         // 15 is the last element of the first leaf
649         // Check that we correctly jump to the next page
650         TupleCursor<Long, String> cursor = btree.browseFrom( 15L );
651         Tuple<Long, String> tuple = cursor.nextKey();
652 
653         assertNotNull( tuple );
654         assertEquals( Long.valueOf( 16 ), tuple.getKey() );
655         assertEquals( "16", tuple.getValue() );
656         cursor.close();
657 
658         // Do the same check, on the revert side : moving backward
659         cursor = btree.browseFrom( 16L );
660         tuple = cursor.prevKey();
661 
662         assertNotNull( tuple );
663         assertEquals( Long.valueOf( 15 ), tuple.getKey() );
664         assertEquals( "15", tuple.getValue() );
665         cursor.close();
666 
667         // Now do a next followed by a prev on the boundary of 2 pages
668         cursor = btree.browseFrom( 16L );
669         tuple = cursor.prevKey();
670 
671         assertNotNull( tuple );
672         assertEquals( Long.valueOf( 15 ), tuple.getKey() );
673         assertEquals( "15", tuple.getValue() );
674 
675         // Move next, we should be back to the initial value
676         assertTrue( cursor.hasNext() );
677         tuple = cursor.next();
678         assertEquals( Long.valueOf( 16 ), tuple.getKey() );
679         assertEquals( "16", tuple.getValue() );
680         cursor.close();
681 
682         // test the extremes of the BTree instead of that of leaves
683         cursor = btree.browseFrom( 30L );
684         tuple = cursor.nextKey();
685         assertFalse( cursor.hasNext() );
686         assertTrue( cursor.hasPrev() );
687 
688         assertEquals( Long.valueOf( 31 ), tuple.getKey() );
689         assertEquals( "31", tuple.getValue() );
690         cursor.close();
691 
692         cursor = btree.browse();
693         assertTrue( cursor.hasNext() );
694         assertFalse( cursor.hasPrev() );
695 
696         tuple = cursor.nextKey();
697         assertEquals( Long.valueOf( 0 ), tuple.getKey() );
698         assertEquals( "0", tuple.getValue() );
699         cursor.close();
700     }
701 
702 
703     @Test
704     public void testNextAfterPrev() throws Exception
705     {
706         int i = 32;
707 
708         for ( int k = 0; k < i; k++ )
709         {
710             btree.insert( ( long ) k, String.valueOf( k ) );
711         }
712 
713         // 15 is the last element of the first leaf
714         TupleCursor<Long, String> cursor = btree.browseFrom( 16L );
715 
716         assertTrue( cursor.hasNext() );
717         Tuple<Long, String> tuple = cursor.next();
718         assertEquals( Long.valueOf( 16 ), tuple.getKey() );
719         assertEquals( "16", tuple.getValue() );
720 
721         assertTrue( cursor.hasPrev() );
722         tuple = cursor.prev();
723         assertEquals( Long.valueOf( 15 ), tuple.getKey() );
724         assertEquals( "15", tuple.getValue() );
725 
726         assertTrue( cursor.hasNext() );
727         tuple = cursor.next();
728         assertEquals( Long.valueOf( 16 ), tuple.getKey() );
729         assertEquals( "16", tuple.getValue() );
730         cursor.close();
731 
732     }
733 
734 
735     /**
736      * Test for moving after a key and traversing backwards.
737      *
738      * @throws Exception
739      */
740     @Test
741     public void testMoveToNextAndTraverseBackward() throws Exception
742     {
743         int i = 5;
744 
745         for ( int k = 0; k < i; k++ )
746         {
747             btree.insert( ( long ) k, Long.toString( k ) );
748         }
749 
750         // 4 is the last element in the tree
751         TupleCursor<Long, String> cursor = btree.browseFrom( 4L );
752         cursor.nextKey();
753 
754         long currentKey = 4L;
755 
756         while ( cursor.hasPrev() )
757         {
758             assertEquals( Long.valueOf( currentKey ), cursor.prev().getKey() );
759             currentKey--;
760         }
761 
762         cursor.close();
763     }
764 
765 
766     /**
767      * Test for moving after a key and traversing backwards.
768      *
769      * @throws Exception
770      */
771     @Test
772     public void testMoveToPrevAndTraverseForward() throws Exception
773     {
774         int i = 5;
775 
776         for ( int k = 0; k < i; k++ )
777         {
778             btree.insert( ( long ) k, Long.toString( k ) );
779         }
780 
781         // 4 is the last element in the tree
782         TupleCursor<Long, String> cursor = btree.browseFrom( 0L );
783 
784         long currentKey = 0L;
785 
786         while ( cursor.hasNext() )
787         {
788             assertEquals( Long.valueOf( currentKey ), cursor.next().getKey() );
789             currentKey++;
790         }
791 
792         cursor.close();
793     }
794 
795 
796     /**
797      * Test that a BTree which forbid duplicate values does not accept them
798      */
799     @Test(expected = DuplicateValueNotAllowedException.class)
800     public void testBTreeForbidDups() throws IOException, BTreeAlreadyManagedException
801     {
802         BTree<Long, String> singleValueBtree = recordManager1.addBTree( "test2", LongSerializer.INSTANCE,
803             StringSerializer.INSTANCE, BTree.FORBID_DUPLICATES );
804 
805         for ( long i = 0; i < 64; i++ )
806         {
807             singleValueBtree.insert( i, Long.toString( i ) );
808         }
809 
810         try
811         {
812             singleValueBtree.insert( 18L, "Duplicate" );
813             fail();
814         }
815         finally
816         {
817             singleValueBtree.close();
818         }
819     }
820 }