1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.ldap.server.partition.impl.btree.jdbm;
18
19
20 import java.io.File;
21 import java.io.IOException;
22 import java.math.BigInteger;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.Map;
27
28 import javax.naming.Name;
29 import javax.naming.NamingEnumeration;
30 import javax.naming.NamingException;
31 import javax.naming.directory.Attribute;
32 import javax.naming.directory.Attributes;
33 import javax.naming.directory.DirContext;
34 import javax.naming.directory.ModificationItem;
35
36 import jdbm.RecordManager;
37 import jdbm.helper.MRU;
38 import jdbm.recman.BaseRecordManager;
39 import jdbm.recman.CacheRecordManager;
40
41 import org.apache.ldap.common.MultiException;
42 import org.apache.ldap.common.exception.LdapNameNotFoundException;
43 import org.apache.ldap.common.exception.LdapSchemaViolationException;
44 import org.apache.ldap.common.message.LockableAttributesImpl;
45 import org.apache.ldap.common.message.ResultCodeEnum;
46 import org.apache.ldap.common.name.LdapName;
47 import org.apache.ldap.common.schema.AttributeType;
48 import org.apache.ldap.common.schema.Normalizer;
49 import org.apache.ldap.common.util.NamespaceTools;
50 import org.apache.ldap.server.configuration.ContextPartitionConfiguration;
51 import org.apache.ldap.server.jndi.ContextFactoryConfiguration;
52 import org.apache.ldap.server.partition.ContextPartition;
53 import org.apache.ldap.server.partition.impl.btree.BTreeContextPartition;
54 import org.apache.ldap.server.partition.impl.btree.Index;
55 import org.apache.ldap.server.partition.impl.btree.IndexAssertion;
56 import org.apache.ldap.server.partition.impl.btree.IndexAssertionEnumeration;
57 import org.apache.ldap.server.partition.impl.btree.IndexNotFoundException;
58 import org.apache.ldap.server.partition.impl.btree.IndexRecord;
59
60
61 /***
62 * A {@link ContextPartition} that stores entries in
63 * <a href="http://jdbm.sourceforge.net/">JDBM</a> database.
64 *
65 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
66 * @version $Rev: 226451 $
67 */
68 public class JdbmContextPartition extends BTreeContextPartition
69 {
70 /*** the JDBM record manager used by this database */
71 private RecordManager recMan;
72 /*** the user provided suffix of this backend database */
73 private Name upSuffix;
74 /*** the normalized suffix of this backend database */
75 private Name normSuffix;
76 /*** the working directory to use for files */
77 private File workingDirectory;
78 /*** the master table storing entries by primary key */
79 private JdbmMasterTable master;
80 /*** a map of attribute names to user indices */
81 private Map indices;
82 /*** a map of index names to system indices */
83 private Map sysIndices;
84
85 /*** true if open */
86 private boolean initialized;
87
88 /*** the normalized distinguished name index */
89 private Index ndnIdx;
90 /*** the user provided distinguished name index */
91 private Index updnIdx;
92 /*** the attribute existance index */
93 private Index existanceIdx;
94 /*** the parent child relationship index */
95 private Index hierarchyIdx;
96 /*** the one level scope alias index */
97 private Index oneAliasIdx;
98 /*** the subtree scope alias index */
99 private Index subAliasIdx;
100 /*** a system index on aliasedObjectName attribute */
101 private Index aliasIdx;
102
103
104
105
106
107
108
109 /***
110 * Creates a store based on JDBM B+Trees.
111 */
112 public JdbmContextPartition()
113 {
114 }
115
116 public synchronized void init( ContextFactoryConfiguration factoryCfg, ContextPartitionConfiguration cfg ) throws NamingException
117 {
118 this.upSuffix = new LdapName( cfg.getSuffix() );
119 this.normSuffix = cfg.getNormalizedSuffix( factoryCfg.getGlobalRegistries().getMatchingRuleRegistry() );
120
121 File workingDirectory = new File(
122 factoryCfg.getStartupConfiguration().getWorkingDirectory().getPath() +
123 File.separator + cfg.getName() );
124
125 workingDirectory.mkdirs();
126
127 this.workingDirectory = workingDirectory;
128
129 try
130 {
131 String path = workingDirectory.getPath() + File.separator + "master";
132 BaseRecordManager base = new BaseRecordManager( path );
133 base.disableTransactions();
134 recMan = new CacheRecordManager( base, new MRU( 1000 ) );
135 }
136 catch ( IOException e )
137 {
138 NamingException ne = new NamingException(
139 "Could not initialize RecordManager" );
140 ne.setRootCause( e );
141 throw ne;
142 }
143
144 master = new JdbmMasterTable( recMan );
145 indices = new HashMap();
146 sysIndices = new HashMap();
147
148 super.init( factoryCfg, cfg );
149 initialized = true;
150 }
151
152
153 public synchronized void destroy()
154 {
155 if ( !initialized )
156 {
157 return;
158 }
159
160 ArrayList array = new ArrayList();
161 array.addAll( indices.values() );
162
163 if ( null != ndnIdx )
164 {
165 array.add( ndnIdx );
166 }
167
168 if ( null != updnIdx )
169 {
170 array.add( updnIdx );
171 }
172
173 if ( null != aliasIdx )
174 {
175 array.add( aliasIdx );
176 }
177
178 if ( null != oneAliasIdx )
179 {
180 array.add( oneAliasIdx );
181 }
182
183 if ( null != subAliasIdx )
184 {
185 array.add( subAliasIdx );
186 }
187
188 if ( null != hierarchyIdx )
189 {
190 array.add( hierarchyIdx );
191 }
192
193 if ( null != existanceIdx )
194 {
195 array.add( existanceIdx );
196 }
197
198 Iterator list = array.iterator();
199 MultiException rootCause = null;
200
201 while ( list.hasNext() )
202 {
203 Index index = ( Index ) list.next();
204
205 try
206 {
207 index.close();
208 }
209 catch ( Throwable t )
210 {
211 if ( null == rootCause )
212 {
213 rootCause = new MultiException();
214 }
215
216 rootCause.addThrowable( t );
217 }
218 }
219
220 try
221 {
222 master.close();
223 }
224 catch ( Throwable t )
225 {
226 if ( null == rootCause )
227 {
228 rootCause = new MultiException();
229 }
230
231 rootCause.addThrowable( t );
232 }
233
234 try
235 {
236 recMan.close();
237 }
238 catch ( Throwable t )
239 {
240 if ( null == rootCause )
241 {
242 rootCause = new MultiException();
243 }
244
245 rootCause.addThrowable( t );
246 }
247
248 initialized = false;
249
250 if ( null != rootCause )
251 {
252 NamingException ne = new NamingException( "Failed to close all" );
253 ne.setRootCause( rootCause );
254 ne.printStackTrace();
255 }
256 }
257
258
259 public boolean isInitialized()
260 {
261 return initialized;
262 }
263
264
265 public synchronized void sync() throws NamingException
266 {
267 if( !initialized )
268 {
269 return;
270 }
271
272 ArrayList array = new ArrayList();
273 array.addAll( indices.values() );
274 array.add( ndnIdx );
275 array.add( updnIdx );
276 array.add( aliasIdx );
277 array.add( oneAliasIdx );
278 array.add( subAliasIdx );
279 array.add( hierarchyIdx );
280 array.add( existanceIdx );
281
282 Iterator list = array.iterator();
283 MultiException rootCause = null;
284
285
286 while ( list.hasNext() )
287 {
288 Index idx = ( Index ) list.next();
289
290 try
291 {
292 idx.sync();
293 }
294 catch ( Throwable t )
295 {
296 t.printStackTrace();
297 if ( null == rootCause )
298 {
299 rootCause = new MultiException();
300 }
301
302 rootCause.addThrowable( t );
303 }
304 }
305
306 try
307 {
308 master.sync();
309 recMan.commit();
310 }
311 catch ( Throwable t )
312 {
313 t.printStackTrace();
314 if ( null == rootCause )
315 {
316 rootCause = new MultiException();
317 }
318
319 rootCause.addThrowable( t );
320 }
321
322 if ( null != rootCause )
323 {
324 NamingException ne = new NamingException( "Failed to sync all" );
325 ne.setRootCause( rootCause );
326 throw ne;
327 }
328 }
329
330
331
332
333
334
335
336 public void addIndexOn( AttributeType spec ) throws NamingException
337 {
338 Index idx = new JdbmIndex( spec, workingDirectory );
339 indices.put( spec.getName().toLowerCase(), idx );
340 }
341
342
343 public Index getExistanceIndex()
344 {
345 return existanceIdx;
346 }
347
348
349 public void setExistanceIndexOn( AttributeType attrType ) throws NamingException
350 {
351 if ( existanceIdx != null )
352 {
353 NamingException e = new NamingException( "Index already set!" );
354 throw e;
355 }
356
357 existanceIdx = new JdbmIndex( attrType, workingDirectory );
358 sysIndices.put( attrType.getName().toLowerCase(), existanceIdx );
359 }
360
361
362 public Index getHierarchyIndex()
363 {
364 return hierarchyIdx;
365 }
366
367
368 public void setHierarchyIndexOn( AttributeType attrType ) throws NamingException
369 {
370 if ( hierarchyIdx != null )
371 {
372 NamingException e = new NamingException( "Index already set!" );
373 throw e;
374 }
375
376 hierarchyIdx = new JdbmIndex( attrType, workingDirectory );
377 sysIndices.put( attrType.getName().toLowerCase(), hierarchyIdx );
378 }
379
380
381 public Index getAliasIndex()
382 {
383 return aliasIdx;
384 }
385
386
387 public void setAliasIndexOn( AttributeType attrType ) throws NamingException
388 {
389 if ( aliasIdx != null )
390 {
391 NamingException e = new NamingException( "Index already set!" );
392 throw e;
393 }
394
395 aliasIdx = new JdbmIndex( attrType, workingDirectory );
396 sysIndices.put( attrType.getName().toLowerCase(), aliasIdx );
397 }
398
399
400 public Index getOneAliasIndex()
401 {
402 return oneAliasIdx;
403 }
404
405
406 public void setOneAliasIndexOn( AttributeType attrType ) throws NamingException
407 {
408 if ( oneAliasIdx != null )
409 {
410 NamingException e = new NamingException( "Index already set!" );
411 throw e;
412 }
413
414 oneAliasIdx = new JdbmIndex( attrType, workingDirectory );
415 sysIndices.put( attrType.getName().toLowerCase(), oneAliasIdx );
416 }
417
418
419 public Index getSubAliasIndex()
420 {
421 return subAliasIdx;
422 }
423
424
425 public void setSubAliasIndexOn( AttributeType attrType ) throws NamingException
426 {
427 if ( subAliasIdx != null )
428 {
429 NamingException e = new NamingException( "Index already set!" );
430 throw e;
431 }
432
433 subAliasIdx = new JdbmIndex( attrType, workingDirectory );
434 sysIndices.put( attrType.getName().toLowerCase(), subAliasIdx );
435 }
436
437
438 public Index getUpdnIndex()
439 {
440 return updnIdx;
441 }
442
443
444 public void setUpdnIndexOn( AttributeType attrType ) throws NamingException
445 {
446 if ( updnIdx != null )
447 {
448 NamingException e = new NamingException( "Index already set!" );
449 throw e;
450 }
451
452 updnIdx = new JdbmIndex( attrType, workingDirectory );
453 sysIndices.put( attrType.getName().toLowerCase(), updnIdx );
454 }
455
456
457 public Index getNdnIndex()
458 {
459 return ndnIdx;
460 }
461
462
463 public void setNdnIndexOn( AttributeType attrType ) throws NamingException
464 {
465 if ( ndnIdx != null )
466 {
467 NamingException e = new NamingException( "Index already set!" );
468 throw e;
469 }
470
471 ndnIdx = new JdbmIndex( attrType, workingDirectory );
472 sysIndices.put( attrType.getName().toLowerCase(), ndnIdx );
473 }
474
475
476 public Iterator getUserIndices()
477 {
478 return indices.keySet().iterator();
479 }
480
481
482 public Iterator getSystemIndices()
483 {
484 return sysIndices.keySet().iterator();
485 }
486
487
488 public boolean hasUserIndexOn( String attribute )
489 {
490 return indices.containsKey( attribute ) ||
491 indices.containsKey( attribute.toLowerCase() );
492 }
493
494
495 public boolean hasSystemIndexOn( String attribute )
496 {
497 return sysIndices.containsKey( attribute ) ||
498 sysIndices.containsKey( attribute.toLowerCase() );
499 }
500
501
502 /***
503 * @todo replace lookups to use the OID instead of the name. Also note
504 * that the OID registry can be used to go between names and oids.
505 *
506 * @see org.apache.ldap.server.partition.impl.btree.BTreeContextPartition#getUserIndex(String)
507 */
508 public Index getUserIndex( String attribute ) throws IndexNotFoundException
509 {
510 String lowerCased = attribute.toLowerCase();
511
512 if ( indices.containsKey( attribute ) )
513 {
514 return ( Index ) indices.get( attribute );
515 }
516 else if ( indices.containsKey( lowerCased ) )
517 {
518 return ( Index ) indices.get( lowerCased );
519 }
520 else
521 {
522 throw new IndexNotFoundException( "An index on attribute " +
523 attribute + " does not exist!" );
524 }
525 }
526
527
528 /***
529 * @todo replace lookups to use the OID instead of the name. Also note
530 * that the OID registry can be used to go between names and oids.
531 *
532 * @see BTreeContextPartition#getEntryId(String)
533 */
534 public Index getSystemIndex( String indexName ) throws IndexNotFoundException
535 {
536 String lowerCased = indexName.toLowerCase();
537
538 if ( sysIndices.containsKey( indexName ) )
539 {
540 return ( Index ) sysIndices.get( indexName );
541 }
542 else if ( sysIndices.containsKey( lowerCased ) )
543 {
544 return ( Index ) sysIndices.get( lowerCased );
545 }
546 else
547 {
548 throw new IndexNotFoundException( "A system index by the name of " +
549 indexName + " does not exist!" );
550 }
551 }
552
553
554 public BigInteger getEntryId( String dn ) throws NamingException
555 {
556 return ndnIdx.forwardLookup( dn );
557 }
558
559
560 public String getEntryDn( BigInteger id ) throws NamingException
561 {
562 return ( String ) ndnIdx.reverseLookup( id );
563 }
564
565
566 public BigInteger getParentId( String dn ) throws NamingException
567 {
568 BigInteger childId = ndnIdx.forwardLookup( dn );
569 return ( BigInteger ) hierarchyIdx.reverseLookup( childId );
570 }
571
572
573 public BigInteger getParentId( BigInteger childId ) throws NamingException
574 {
575 return ( BigInteger ) hierarchyIdx.reverseLookup( childId );
576 }
577
578
579 public String getEntryUpdn( BigInteger id ) throws NamingException
580 {
581 return ( String ) updnIdx.reverseLookup( id );
582 }
583
584
585 public String getEntryUpdn( String dn ) throws NamingException
586 {
587 BigInteger id = ndnIdx.forwardLookup( dn );
588 return ( String ) updnIdx.reverseLookup( id );
589 }
590
591
592 public int count() throws NamingException
593 {
594 return master.count();
595 }
596
597
598 /***
599 * Removes the index entries for an alias before the entry is deleted from
600 * the master table.
601 *
602 * @todo Optimize this by walking the hierarchy index instead of the name
603 * @param aliasId the id of the alias entry in the master table
604 * @throws NamingException if we cannot delete the indices
605 */
606 private void dropAliasIndices( BigInteger aliasId ) throws NamingException
607 {
608 String targetDn = ( String ) aliasIdx.reverseLookup( aliasId );
609 BigInteger targetId = getEntryId( targetDn );
610 String aliasDn = getEntryDn( aliasId );
611 Name ancestorDn = new LdapName( aliasDn ).getSuffix( 1 );
612 BigInteger ancestorId = getEntryId( ancestorDn.toString() );
613
614
615
616
617
618
619
620
621
622
623
624
625 oneAliasIdx.drop( ancestorId, targetId );
626 subAliasIdx.drop( ancestorId, targetId );
627
628 while ( ! ancestorDn.equals( upSuffix ) )
629 {
630 ancestorDn = ancestorDn.getSuffix( 1 );
631 ancestorId = getEntryId( ancestorDn.toString() );
632
633 subAliasIdx.drop( ancestorId, targetId );
634 }
635
636
637 aliasIdx.drop( aliasId );
638 }
639
640
641 /***
642 * Adds indices for an aliasEntry to be added to the database while checking
643 * for constrained alias constructs like alias cycles and chaining.
644 *
645 * @param aliasDn normalized distinguished name for the alias entry
646 * @param aliasTarget the user provided aliased entry dn as a string
647 * @param aliasId the id of alias entry to add
648 * @throws NamingException if index addition fails, of the alias is not
649 * allowed due to chaining or cycle formation.
650 */
651 private void addAliasIndices( BigInteger aliasId, Name aliasDn,
652 String aliasTarget ) throws NamingException
653 {
654 Name targetDn = null;
655 BigInteger targetId = null;
656 Normalizer normalizer = null;
657 Name ancestorDn = null;
658 BigInteger ancestorId = null;
659
660
661 normalizer = oneAliasIdx.getAttribute().getEquality().getNormalizer();
662 targetDn = new LdapName( ( String ) normalizer.normalize( aliasTarget ) );
663
664
665
666
667
668
669
670
671
672
673
674 if ( aliasDn.startsWith( targetDn ) )
675 {
676 if ( aliasDn.equals( targetDn ) )
677 {
678 throw new NamingException( "[36] aliasDereferencingProblem - "
679 + "attempt to create alias to itself." );
680 }
681
682 throw new NamingException( "[36] aliasDereferencingProblem - "
683 + "attempt to create alias with cycle to relative "
684 + aliasTarget + " not allowed from descendent alias "
685 + aliasDn );
686 }
687
688
689
690
691
692
693
694
695
696 if ( ! targetDn.startsWith( upSuffix ) )
697 {
698
699 throw new NamingException( "[36] aliasDereferencingProblem -"
700 + " the alias points to an entry outside of the " + upSuffix
701 + " namingContext to an object whose existance cannot be"
702 + " determined." );
703 }
704
705
706 targetId = ndnIdx.forwardLookup( targetDn.toString() );
707
708
709
710
711
712
713
714 if ( null == targetId )
715 {
716
717 throw new NamingException( "[33] aliasProblem - "
718 + "the alias when dereferenced would not name a known object "
719 + "the aliasedObjectName must be set to a valid existing "
720 + "entry." );
721 }
722
723
724
725
726
727
728
729
730
731
732
733 if ( null != aliasIdx.reverseLookup( targetId ) )
734 {
735
736 throw new NamingException( "[36] aliasDereferencingProblem -"
737 + " the alias points to another alias. Alias chaining is"
738 + " not supported by this backend." );
739 }
740
741
742 aliasIdx.add( aliasTarget, aliasId );
743
744
745
746
747
748
749
750
751 ancestorDn = aliasDn.getSuffix( 1 );
752 ancestorId = getEntryId( ancestorDn.toString() );
753
754 if ( ! NamespaceTools.isSibling( targetDn, aliasDn ) )
755 {
756 oneAliasIdx.add( ancestorId, targetId );
757 }
758
759
760
761
762
763
764
765
766
767
768
769 while ( ! ancestorDn.equals( upSuffix ) && null != ancestorId )
770 {
771 if ( ! NamespaceTools.isDescendant( ancestorDn, targetDn ) )
772 {
773 subAliasIdx.add( ancestorId, targetId );
774 }
775
776 ancestorDn = ancestorDn.getSuffix( 1 );
777 ancestorId = getEntryId( ancestorDn.toString() );
778 }
779 }
780
781
782 public void add( String updn, Name dn, Attributes entry ) throws NamingException
783 {
784 BigInteger id;
785 BigInteger parentId = null;
786
787 id = master.getNextId();
788
789
790
791
792
793
794
795 if ( dn.equals( normSuffix ) )
796 {
797 parentId = BigInteger.ZERO;
798 }
799 else
800 {
801 parentId = getEntryId( dn.getSuffix( 1 ).toString() );
802 }
803
804
805 if ( parentId == null )
806 {
807 throw new LdapNameNotFoundException( "Id for parent '" + dn.getSuffix( 1 ).toString() + "' not found!" );
808 }
809
810 Attribute objectClass = entry.get( "objectClass" );
811
812 if ( objectClass == null )
813 {
814 String msg = "Entry " + updn + " contains no objectClass attribute: " + entry;
815
816 throw new LdapSchemaViolationException( msg, ResultCodeEnum.OBJECTCLASSVIOLATION );
817 }
818
819
820
821
822 if ( entry.get( "objectClass" ).contains( ContextPartition.ALIAS_OBJECT ) )
823 {
824 addAliasIndices( id, dn, ( String ) entry.get( ContextPartition.ALIAS_ATTRIBUTE ).get() );
825 }
826
827 ndnIdx.add( dn.toString(), id );
828 updnIdx.add( updn, id );
829 hierarchyIdx.add( parentId, id );
830
831
832 NamingEnumeration list = entry.getIDs();
833 while ( list.hasMore() )
834 {
835 String attribute = ( String ) list.next();
836
837 if ( hasUserIndexOn( attribute ) )
838 {
839 Index idx = getUserIndex( attribute );
840 NamingEnumeration values = entry.get( attribute ).getAll();
841
842 while ( values.hasMore() )
843 {
844 idx.add( values.next(), id );
845 }
846
847
848 existanceIdx.add( attribute.toLowerCase(), id );
849 }
850 }
851
852 master.put( entry, id );
853 }
854
855
856 public Attributes lookup( BigInteger id ) throws NamingException
857 {
858 return master.get( id );
859 }
860
861
862 public void delete( BigInteger id ) throws NamingException
863 {
864 Attributes entry = lookup( id );
865 BigInteger parentId = getParentId( id );
866 NamingEnumeration attrs = entry.getIDs();
867
868 if ( entry.get( "objectClass" ).contains( ContextPartition.ALIAS_OBJECT ) )
869 {
870 dropAliasIndices( id );
871 }
872
873 ndnIdx.drop( id );
874 updnIdx.drop( id );
875 hierarchyIdx.drop( id );
876
877
878 if ( ! parentId.equals( BigInteger.ZERO ) )
879 {
880 hierarchyIdx.drop( parentId, id );
881 }
882
883 while ( attrs.hasMore() )
884 {
885 String attr = ( ( String ) attrs.next() );
886
887 if ( hasUserIndexOn( attr ) )
888 {
889 Index index = getUserIndex( attr );
890 NamingEnumeration values = entry.get( attr ).getAll();
891
892 while ( values.hasMore() )
893 {
894 index.drop( values.next(), id );
895 }
896
897 existanceIdx.drop( attr.toLowerCase(), id );
898 }
899 }
900
901 master.delete( id );
902 }
903
904
905 public NamingEnumeration list( BigInteger id ) throws NamingException
906 {
907 return hierarchyIdx.listIndices( id );
908 }
909
910
911 public int getChildCount( BigInteger id ) throws NamingException
912 {
913 return hierarchyIdx.count( id );
914 }
915
916
917 public Name getSuffix( boolean normalized )
918 {
919 if ( normalized )
920 {
921 return normSuffix;
922 }
923
924 return upSuffix;
925 }
926
927
928 public Attributes getSuffixEntry() throws NamingException
929 {
930 BigInteger id = getEntryId( upSuffix.toString() );
931
932 if ( null == id )
933 {
934 return null;
935 }
936
937 return lookup( id );
938 }
939
940
941 public void setProperty( String propertyName, String propertyValue )
942 throws NamingException
943 {
944 master.setProperty( propertyName, propertyValue );
945 }
946
947
948 public String getProperty( String propertyName ) throws NamingException
949 {
950 return master.getProperty( propertyName );
951 }
952
953
954 public Attributes getIndices( BigInteger id ) throws NamingException
955 {
956 Attributes attributes = new LockableAttributesImpl();
957
958
959 attributes.put( "_nDn", getEntryDn( id ) );
960 attributes.put( "_upDn", getEntryUpdn( id ) );
961 attributes.put( "_parent", getParentId( id ) );
962
963
964 Iterator idxList = this.indices.values().iterator();
965 while ( idxList.hasNext() )
966 {
967 Index index = ( Index ) idxList.next();
968 NamingEnumeration list = index.listReverseIndices( id );
969 while ( list.hasMore() )
970 {
971 IndexRecord rec = ( IndexRecord ) list.next();
972 Object val = rec.getIndexKey();
973 attributes.put( index.getAttribute().getName(), val );
974 }
975 }
976
977
978
979 NamingEnumeration list = existanceIdx.listReverseIndices( id );
980 StringBuffer val = new StringBuffer();
981 while ( list.hasMore() )
982 {
983 IndexRecord rec = ( IndexRecord ) list.next();
984 val.append( "_existance[" );
985 val.append( rec.getIndexKey() );
986 val.append( "]" );
987 attributes.put( val.toString(), rec.getEntryId() );
988 val.setLength( 0 );
989 }
990
991
992
993 list = hierarchyIdx.listIndices( id );
994 while ( list.hasMore() )
995 {
996 IndexRecord rec = ( IndexRecord ) list.next();
997 attributes.put( "_child", rec.getEntryId() );
998 }
999
1000 return attributes;
1001 }
1002
1003
1004 /***
1005 * Adds a set of attribute values while affecting the appropriate indices.
1006 * The entry is not persisted: it is only changed in anticipation for a put
1007 * into the master table.
1008 *
1009 * @param id the primary key of the entry
1010 * @param entry the entry to alter
1011 * @param mods the attribute and values to add
1012 * @throws NamingException if index alteration or attribute addition
1013 * fails.
1014 */
1015 private void add( BigInteger id, Attributes entry, Attribute mods )
1016 throws NamingException
1017 {
1018 if ( hasUserIndexOn( mods.getID() ) )
1019 {
1020 Index idx = getUserIndex( mods.getID() );
1021 idx.add( mods, id );
1022
1023
1024 if ( ! existanceIdx.hasValue( mods.getID(), id ) )
1025 {
1026 idx.add( mods.getID(), id );
1027 }
1028 }
1029
1030 entry.put( mods );
1031
1032 if ( mods.getID().equals( ContextPartition.ALIAS_ATTRIBUTE ) )
1033 {
1034 String ndnStr = ( String ) ndnIdx.reverseLookup( id );
1035 addAliasIndices( id, new LdapName( ndnStr ),
1036 ( String ) mods.get() );
1037 }
1038 }
1039
1040
1041 /***
1042 * Completely removes the set of values for an attribute having the values
1043 * supplied while affecting the appropriate indices. The entry is not
1044 * persisted: it is only changed in anticipation for a put into the master
1045 * table. Note that an empty attribute w/o values will remove all the
1046 * values within the entry where as an attribute w/ values will remove those
1047 * attribute values it contains.
1048 *
1049 * @param id the primary key of the entry
1050 * @param entry the entry to alter
1051 * @param mods the attribute and its values to delete
1052 * @throws NamingException if index alteration or attribute modification
1053 * fails.
1054 */
1055 private void remove( BigInteger id, Attributes entry, Attribute mods )
1056 throws NamingException
1057 {
1058 if ( hasUserIndexOn( mods.getID() ) )
1059 {
1060 Index idx = getUserIndex( mods.getID() );
1061 idx.drop( mods, id );
1062
1063
1064
1065
1066
1067 if ( null == idx.reverseLookup( id ) )
1068 {
1069 existanceIdx.drop( mods.getID(), id );
1070 }
1071 }
1072
1073
1074
1075
1076
1077
1078
1079 if ( mods.size() == 0 )
1080 {
1081 entry.remove( mods.getID() );
1082 }
1083 else
1084 {
1085 Attribute entryAttr = entry.get( mods.getID() );
1086 NamingEnumeration values = mods.getAll();
1087 while ( values.hasMore() )
1088 {
1089 entryAttr.remove( values.next() );
1090 }
1091 }
1092
1093
1094 if ( mods.getID().equals( ContextPartition.ALIAS_ATTRIBUTE ) )
1095 {
1096 dropAliasIndices( id );
1097 }
1098 }
1099
1100
1101 /***
1102 * Completely replaces the existing set of values for an attribute with the
1103 * modified values supplied affecting the appropriate indices. The entry
1104 * is not persisted: it is only changed in anticipation for a put into the
1105 * master table.
1106 *
1107 * @param id the primary key of the entry
1108 * @param entry the entry to alter
1109 * @param mods the replacement attribute and values
1110 * @throws NamingException if index alteration or attribute modification
1111 * fails.
1112 */
1113 private void replace( BigInteger id, Attributes entry, Attribute mods )
1114 throws NamingException
1115 {
1116 if ( hasUserIndexOn( mods.getID() ) )
1117 {
1118 Index idx = getUserIndex( mods.getID() );
1119
1120
1121 idx.drop( id );
1122 idx.add( mods, id );
1123
1124
1125
1126
1127
1128 if ( null == idx.reverseLookup( id ) )
1129 {
1130 existanceIdx.drop( mods.getID(), id );
1131 }
1132 }
1133
1134 if ( mods.getID().equals( ContextPartition.ALIAS_ATTRIBUTE ) )
1135 {
1136 dropAliasIndices( id );
1137 }
1138
1139
1140 entry.put( mods );
1141
1142 if ( mods.getID().equals( ContextPartition.ALIAS_ATTRIBUTE ) )
1143 {
1144 String ndnStr = ( String ) ndnIdx.reverseLookup( id );
1145 addAliasIndices( id, new LdapName( ndnStr ),
1146 ( String ) mods.get() );
1147 }
1148 }
1149
1150
1151 public void modify( Name dn, int modOp, Attributes mods ) throws NamingException
1152 {
1153 NamingEnumeration attrs = null;
1154 BigInteger id = getEntryId( dn.toString() );
1155 Attributes entry = master.get( id );
1156
1157 switch ( modOp )
1158 {
1159 case( DirContext.ADD_ATTRIBUTE ):
1160 attrs = mods.getIDs();
1161
1162 while ( attrs.hasMore() )
1163 {
1164 String attrId = ( String ) attrs.next();
1165 Attribute attr = mods.get( attrId );
1166 add( id, entry, attr );
1167 }
1168
1169 break;
1170 case( DirContext.REMOVE_ATTRIBUTE ):
1171 attrs = mods.getIDs();
1172
1173 while ( attrs.hasMore() )
1174 {
1175 String attrId = ( String ) attrs.next();
1176 Attribute attr = mods.get( attrId );
1177 remove( id, entry, attr );
1178 }
1179
1180 break;
1181 case( DirContext.REPLACE_ATTRIBUTE ):
1182 attrs = mods.getIDs();
1183
1184 while ( attrs.hasMore() )
1185 {
1186 String attrId = ( String ) attrs.next();
1187 Attribute attr = mods.get( attrId );
1188 replace( id, entry, attr );
1189 }
1190
1191 break;
1192 default:
1193 throw new NamingException(
1194 "Unidentified modification operation" );
1195 }
1196
1197 master.put( entry, id );
1198 }
1199
1200
1201 public void modify( Name dn, ModificationItem [] mods ) throws NamingException
1202 {
1203 BigInteger id = getEntryId( dn.toString() );
1204 Attributes entry = master.get( id );
1205
1206 for ( int ii = 0; ii < mods.length; ii++ )
1207 {
1208 Attribute attrMods = mods[ii].getAttribute();
1209
1210 switch ( mods[ ii ].getModificationOp() )
1211 {
1212 case( DirContext.ADD_ATTRIBUTE ):
1213 add( id, entry, attrMods );
1214 break;
1215 case( DirContext.REMOVE_ATTRIBUTE ):
1216 remove( id, entry, attrMods );
1217 break;
1218 case( DirContext.REPLACE_ATTRIBUTE ):
1219 replace( id, entry, attrMods );
1220 break;
1221 default:
1222 throw new NamingException(
1223 "Unidentified modification operation" );
1224 }
1225 }
1226
1227 master.put( entry, id );
1228 }
1229
1230
1231 /***
1232 * Changes the relative distinuished name of an entry specified by a
1233 * distinguished name with the optional removal of the old Rdn attribute
1234 * value from the entry. Name changes propagate down as dn changes to the
1235 * descendants of the entry where the Rdn changed.
1236 *
1237 * An Rdn change operation does not change parent child relationships. It
1238 * merely propagates a name change at a point in the DIT where the Rdn is
1239 * changed. The change propagates down the subtree rooted at the
1240 * distinguished name specified.
1241 *
1242 * @param dn the normalized distinguished name of the entry to alter
1243 * @param newRdn the new Rdn to set
1244 * @param deleteOldRdn whether or not to remove the old Rdn attr/val
1245 * @throws NamingException if there are any errors propagating the name
1246 * changes.
1247 */
1248 public void modifyRn( Name dn, String newRdn, boolean deleteOldRdn )
1249 throws NamingException
1250 {
1251 String newRdnAttr = NamespaceTools.getRdnAttribute( newRdn );
1252 String newRdnValue = NamespaceTools.getRdnValue( newRdn );
1253 BigInteger id = getEntryId( dn.toString() );
1254 Attributes entry = lookup( id );
1255 Name updn = new LdapName( getEntryUpdn( id ) );
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266 entry.put( newRdnAttr, newRdnValue );
1267
1268 if ( hasUserIndexOn( newRdnAttr ) )
1269 {
1270 Index idx = getUserIndex( newRdnAttr );
1271 idx.add( newRdnValue, id );
1272
1273
1274 if ( ! existanceIdx.hasValue( newRdnAttr, id ) )
1275 {
1276 existanceIdx.add( newRdnAttr, id );
1277 }
1278 }
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294 if ( deleteOldRdn )
1295 {
1296 String oldRdn = updn.get( updn.size() - 1 );
1297 String oldRdnAttr = NamespaceTools.getRdnAttribute( oldRdn );
1298 String oldRdnValue = NamespaceTools.getRdnValue( oldRdn );
1299
1300 entry.get( oldRdnAttr ).remove( oldRdnValue );
1301
1302 if ( hasUserIndexOn( oldRdnAttr ) )
1303 {
1304 Index idx = getUserIndex( oldRdnAttr );
1305 idx.drop( oldRdnValue, id );
1306
1307
1308
1309
1310
1311 if ( null == idx.reverseLookup( id ) )
1312 {
1313 existanceIdx.drop( oldRdnAttr, id );
1314 }
1315 }
1316 }
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329 Name newUpdn = ( Name ) updn.clone();
1330 newUpdn.remove( newUpdn.size() - 1 );
1331 newUpdn.add( newUpdn.size(), newRdn );
1332 modifyDn( id, newUpdn, false );
1333 }
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343 /***
1344 * Recursively modifies the distinguished name of an entry and the names of
1345 * its descendants calling itself in the recursion.
1346 *
1347 * @param id the primary key of the entry
1348 * @param updn User provided distinguished name to set as the new DN
1349 * @param isMove whether or not the name change is due to a move operation
1350 * which affects alias indices.
1351 * @throws NamingException if something goes wrong
1352 */
1353 private void modifyDn( BigInteger id, Name updn, boolean isMove )
1354 throws NamingException
1355 {
1356 String aliasTarget = null;
1357
1358
1359 ndnIdx.drop( id );
1360 ndnIdx.add( ndnIdx.getNormalized( updn.toString() ), id );
1361
1362 updnIdx.drop( id );
1363 updnIdx.add( updn.toString(), id );
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375 if ( isMove )
1376 {
1377 aliasTarget = ( String ) aliasIdx.reverseLookup( id );
1378
1379 if ( null != aliasTarget )
1380 {
1381 addAliasIndices( id, new LdapName( getEntryDn( id ) ),
1382 aliasTarget );
1383 }
1384 }
1385
1386 NamingEnumeration children = list( id );
1387 while ( children.hasMore() )
1388 {
1389
1390 IndexRecord rec = ( IndexRecord ) children.next();
1391 BigInteger childId = rec.getEntryId();
1392
1393
1394
1395
1396
1397 Name childUpdn = ( Name ) updn.clone();
1398 Name oldUpdn = new LdapName( getEntryUpdn( childId ) );
1399 String rdn = LdapName.getRdn( oldUpdn );
1400 childUpdn.add( childUpdn.size(), rdn );
1401
1402
1403 modifyDn( childId, childUpdn, isMove );
1404 }
1405 }
1406
1407
1408 public void move( Name oldChildDn, Name newParentDn, String newRdn,
1409 boolean deleteOldRdn ) throws NamingException
1410 {
1411 modifyRn( oldChildDn, newRdn, deleteOldRdn );
1412 move( oldChildDn, newParentDn );
1413 }
1414
1415
1416 /***
1417 * Moves an entry under a new parent. The operation causes a shift in the
1418 * parent child relationships between the old parent, new parent and the
1419 * child moved. All other descendant entries under the child never change
1420 * their direct parent child relationships. Hence after the parent child
1421 * relationship changes are broken at the old parent and set at the new
1422 * parent a modifyDn operation is conducted to handle name changes
1423 * propagating down through the moved child and its descendants.
1424 *
1425 * @param oldChildDn the normalized dn of the child to be moved
1426 * @param newParentDn the normalized dn of the new parent for the child
1427 * @throws NamingException if something goes wrong
1428 */
1429 public void move( Name oldChildDn, Name newParentDn ) throws NamingException
1430 {
1431
1432 BigInteger childId = getEntryId( oldChildDn.toString() );
1433 BigInteger newParentId = getEntryId( newParentDn.toString() );
1434 BigInteger oldParentId = getParentId( childId );
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444 dropMovedAliasIndices( oldChildDn );
1445
1446
1447
1448
1449
1450 hierarchyIdx.drop( oldParentId, childId );
1451 hierarchyIdx.add( newParentId, childId );
1452
1453
1454
1455
1456
1457
1458 Name childUpdn = new LdapName( getEntryUpdn( childId ) );
1459 String childRdn = childUpdn.get( childUpdn.size() - 1 );
1460 Name newUpdn = new LdapName( getEntryUpdn( newParentId ) );
1461 newUpdn.add( newUpdn.size(), childRdn );
1462
1463
1464 modifyDn( childId, newUpdn, true );
1465 }
1466
1467
1468 /***
1469 * For all aliases including and under the moved base, this method removes
1470 * one and subtree alias index tuples for old ancestors above the moved base
1471 * that will no longer be ancestors after the move.
1472 *
1473 * @param movedBase the base at which the move occured - the moved node
1474 * @throws NamingException if system indices fail
1475 */
1476 private void dropMovedAliasIndices( final Name movedBase ) throws NamingException
1477 {
1478
1479 IndexAssertion isBaseDescendant = new IndexAssertion()
1480 {
1481 public boolean assertCandidate( IndexRecord rec )
1482 throws NamingException
1483 {
1484 String dn = getEntryDn( rec.getEntryId() );
1485 if ( dn.endsWith( movedBase.toString() ) )
1486 {
1487 return true;
1488 }
1489
1490 return false;
1491 }
1492 };
1493
1494 BigInteger movedBaseId = getEntryId( movedBase.toString() );
1495 if ( aliasIdx.reverseLookup( movedBaseId ) != null )
1496 {
1497 dropAliasIndices( movedBaseId, movedBase );
1498 }
1499
1500 NamingEnumeration aliases = new IndexAssertionEnumeration(
1501 aliasIdx.listIndices( movedBase.toString(), true ),
1502 isBaseDescendant );
1503 while ( aliases.hasMore() )
1504 {
1505 IndexRecord entry = ( IndexRecord ) aliases.next();
1506 dropAliasIndices( entry.getEntryId(), movedBase );
1507 }
1508 }
1509
1510
1511 /***
1512 * For the alias id all ancestor one and subtree alias tuples are moved
1513 * above the moved base.
1514 *
1515 * @param aliasId the id of the alias
1516 * @param movedBase the base where the move occured
1517 * @throws NamingException if indices fail
1518 */
1519 private void dropAliasIndices( BigInteger aliasId, Name movedBase )
1520 throws NamingException
1521 {
1522 String targetDn = ( String ) aliasIdx.reverseLookup( aliasId );
1523 BigInteger targetId = getEntryId( targetDn );
1524 String aliasDn = getEntryDn( aliasId );
1525
1526
1527
1528
1529
1530 Name ancestorDn = movedBase.getSuffix( 1 );
1531 BigInteger ancestorId = getEntryId( ancestorDn.toString() );
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545 if ( aliasDn.equals( movedBase.toString() ) )
1546 {
1547 oneAliasIdx.drop( ancestorId, targetId );
1548 }
1549
1550 subAliasIdx.drop( ancestorId, targetId );
1551
1552 while ( ! ancestorDn.equals( upSuffix ) )
1553 {
1554 ancestorDn = ancestorDn.getSuffix( 1 );
1555 ancestorId = getEntryId( ancestorDn.toString() );
1556
1557 subAliasIdx.drop( ancestorId, targetId );
1558 }
1559 }
1560 }
1561