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 */
020 package org.apache.directory.server.core.partition.impl.btree;
021
022
023 import java.io.File;
024 import java.util.Collections;
025 import java.util.HashSet;
026 import java.util.Iterator;
027 import java.util.Set;
028
029 import javax.naming.directory.SearchControls;
030
031 import org.apache.directory.server.constants.ApacheSchemaConstants;
032 import org.apache.directory.server.core.entry.ClonedServerEntry;
033 import org.apache.directory.server.core.filtering.BaseEntryFilteringCursor;
034 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
035 import org.apache.directory.server.core.interceptor.context.AddOperationContext;
036 import org.apache.directory.server.core.interceptor.context.DeleteOperationContext;
037 import org.apache.directory.server.core.interceptor.context.EntryOperationContext;
038 import org.apache.directory.server.core.interceptor.context.ListOperationContext;
039 import org.apache.directory.server.core.interceptor.context.LookupOperationContext;
040 import org.apache.directory.server.core.interceptor.context.ModifyOperationContext;
041 import org.apache.directory.server.core.interceptor.context.MoveAndRenameOperationContext;
042 import org.apache.directory.server.core.interceptor.context.MoveOperationContext;
043 import org.apache.directory.server.core.interceptor.context.RenameOperationContext;
044 import org.apache.directory.server.core.interceptor.context.SearchOperationContext;
045 import org.apache.directory.server.core.partition.AbstractPartition;
046 import org.apache.directory.server.core.partition.Partition;
047 import org.apache.directory.server.i18n.I18n;
048 import org.apache.directory.server.xdbm.Index;
049 import org.apache.directory.server.xdbm.IndexCursor;
050 import org.apache.directory.server.xdbm.search.Optimizer;
051 import org.apache.directory.server.xdbm.search.SearchEngine;
052 import org.apache.directory.shared.ldap.entry.ServerEntry;
053 import org.apache.directory.shared.ldap.exception.LdapContextNotEmptyException;
054 import org.apache.directory.shared.ldap.exception.LdapInvalidDnException;
055 import org.apache.directory.shared.ldap.exception.LdapNoSuchObjectException;
056 import org.apache.directory.shared.ldap.name.DN;
057 import org.apache.directory.shared.ldap.schema.AttributeType;
058 import org.apache.directory.shared.ldap.schema.SchemaManager;
059
060
061 /**
062 * An abstract {@link Partition} that uses general BTree operations.
063 *
064 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
065 * @version $Rev: 927146 $
066 */
067 public abstract class BTreePartition<ID> extends AbstractPartition
068 {
069 protected static final Set<String> SYS_INDEX_OIDS;
070
071 static
072 {
073 Set<String> set = new HashSet<String>();
074 set.add( ApacheSchemaConstants.APACHE_ALIAS_AT_OID );
075 set.add( ApacheSchemaConstants.APACHE_EXISTENCE_AT_OID );
076 set.add( ApacheSchemaConstants.APACHE_ONE_LEVEL_AT_OID );
077 set.add( ApacheSchemaConstants.APACHE_N_DN_AT_OID );
078 set.add( ApacheSchemaConstants.APACHE_ONE_ALIAS_AT_OID );
079 set.add( ApacheSchemaConstants.APACHE_SUB_ALIAS_AT_OID );
080 set.add( ApacheSchemaConstants.APACHE_UP_DN_AT_OID );
081 //set.add( ApacheSchemaConstants.ENTRY_CSN_AT_OID );
082 //set.add( ApacheSchemaConstants.ENTRY_UUID_AT_OID );
083 //set.add( SchemaConstants.OBJECT_CLASS_AT_OID );
084 SYS_INDEX_OIDS = Collections.unmodifiableSet( set );
085 }
086
087 /** the search engine used to search the database */
088 protected SearchEngine<ServerEntry, ID> searchEngine;
089 protected Optimizer optimizer;
090
091 protected SchemaManager schemaManager;
092
093 protected String id;
094 protected int cacheSize = -1;
095 protected DN suffix;
096 private File partitionDir;
097
098 /** The rootDSE context */
099 protected ServerEntry contextEntry;
100 private Set<Index<? extends Object, ServerEntry, ID>> indexedAttributes;
101
102
103 // ------------------------------------------------------------------------
104 // C O N S T R U C T O R S
105 // ------------------------------------------------------------------------
106
107 /**
108 * Creates a B-tree based context partition.
109 */
110 protected BTreePartition()
111 {
112 indexedAttributes = new HashSet<Index<? extends Object, ServerEntry, ID>>();
113 }
114
115
116 // ------------------------------------------------------------------------
117 // C O N F I G U R A T I O N M E T H O D S
118 // ------------------------------------------------------------------------
119
120 /**
121 * {@inheritDoc}
122 */
123 public void setSchemaManager( SchemaManager schemaManager )
124 {
125 this.schemaManager = schemaManager;
126 }
127
128
129 /**
130 * {@inheritDoc}
131 */
132 public SchemaManager getSchemaManager()
133 {
134 return schemaManager;
135 }
136
137
138 /**
139 * Gets the directory in which this Partition stores files.
140 *
141 * @return the directory in which this Partition stores files.
142 */
143 public File getPartitionDir()
144 {
145 return partitionDir;
146 }
147
148
149 /**
150 * Sets the directory in which this Partition stores files.
151 *
152 * @param partitionDir the directory in which this Partition stores files.
153 */
154 public void setPartitionDir( File partitionDir )
155 {
156 this.partitionDir = partitionDir;
157 }
158
159
160 public void setIndexedAttributes( Set<Index<? extends Object, ServerEntry, ID>> indexedAttributes )
161 {
162 this.indexedAttributes = indexedAttributes;
163 }
164
165
166 public void addIndexedAttributes( Index<? extends Object, ServerEntry, ID>... indexes )
167 {
168 for ( Index<? extends Object, ServerEntry, ID> index : indexes )
169 {
170 indexedAttributes.add( index );
171 }
172 }
173
174
175 public Set<Index<? extends Object, ServerEntry, ID>> getIndexedAttributes()
176 {
177 return indexedAttributes;
178 }
179
180
181 /**
182 * Used to specify the entry cache size for a Partition. Various Partition
183 * implementations may interpret this value in different ways: i.e. total cache
184 * size limit verses the number of entries to cache.
185 *
186 * @param cacheSize the maximum size of the cache in the number of entries
187 */
188 public void setCacheSize( int cacheSize )
189 {
190 this.cacheSize = cacheSize;
191 }
192
193
194 /**
195 * Gets the entry cache size for this BTreePartition.
196 *
197 * @return the maximum size of the cache as the number of entries maximum before paging out
198 */
199 public int getCacheSize()
200 {
201 return cacheSize;
202 }
203
204
205 /**
206 * Gets the unique identifier for this partition.
207 *
208 * @return the unique identifier for this partition
209 */
210 public String getId()
211 {
212 return id;
213 }
214
215
216 /**
217 * Sets the unique identifier for this partition.
218 *
219 * @param id the unique identifier for this partition
220 */
221 public void setId( String id )
222 {
223 this.id = id;
224 }
225
226
227 // -----------------------------------------------------------------------
228 // E N D C O N F I G U R A T I O N M E T H O D S
229 // -----------------------------------------------------------------------
230
231 // ------------------------------------------------------------------------
232 // Public Accessors - not declared in any interfaces just for this class
233 // ------------------------------------------------------------------------
234
235 /**
236 * Gets the DefaultSearchEngine used by this ContextPartition to search the
237 * Database.
238 *
239 * @return the search engine
240 */
241 public SearchEngine<ServerEntry, ID> getSearchEngine()
242 {
243 return searchEngine;
244 }
245
246
247 // ------------------------------------------------------------------------
248 // Partition Interface Method Implementations
249 // ------------------------------------------------------------------------
250
251 /**
252 * {@inheritDoc}
253 */
254 public void delete( DeleteOperationContext opContext ) throws Exception
255 {
256 DN dn = opContext.getDn();
257
258 ID id = getEntryId( dn.getNormName() );
259
260 // don't continue if id is null
261 if ( id == null )
262 {
263 throw new LdapNoSuchObjectException( I18n.err( I18n.ERR_699, dn ) );
264 }
265
266 if ( getChildCount( id ) > 0 )
267 {
268 LdapContextNotEmptyException cnee = new LdapContextNotEmptyException( I18n.err( I18n.ERR_700, dn ) );
269 //cnee.setRemainingName( dn );
270 throw cnee;
271 }
272
273 delete( id );
274 }
275
276
277 public abstract void add( AddOperationContext opContext ) throws Exception;
278
279
280 public abstract void modify( ModifyOperationContext opContext ) throws Exception;
281
282
283 public EntryFilteringCursor list( ListOperationContext opContext ) throws Exception
284 {
285 return new BaseEntryFilteringCursor( new ServerEntryCursorAdaptor<ID>( this, list( getEntryId( opContext
286 .getDn().getNormName() ) ) ), opContext );
287 }
288
289
290 public EntryFilteringCursor search( SearchOperationContext opContext ) throws Exception
291 {
292 SearchControls searchCtls = opContext.getSearchControls();
293 IndexCursor<ID, ServerEntry, ID> underlying;
294
295 underlying = searchEngine.cursor( opContext.getDn(), opContext.getAliasDerefMode(), opContext.getFilter(),
296 searchCtls );
297
298 return new BaseEntryFilteringCursor( new ServerEntryCursorAdaptor<ID>( this, underlying ), opContext );
299 }
300
301
302 public ClonedServerEntry lookup( LookupOperationContext opContext ) throws Exception
303 {
304 ID id = getEntryId( opContext.getDn().getNormName() );
305
306 if ( id == null )
307 {
308 return null;
309 }
310
311 ClonedServerEntry entry = lookup( id );
312
313 if ( ( opContext.getAttrsId() == null ) || ( opContext.getAttrsId().size() == 0 ) )
314 {
315 return entry;
316 }
317
318 for ( AttributeType attributeType : ( ( ServerEntry ) entry.getOriginalEntry() ).getAttributeTypes() )
319 {
320 if ( !opContext.getAttrsId().contains( attributeType.getOid() ) )
321 {
322 entry.removeAttributes( attributeType );
323 }
324 }
325
326 return entry;
327 }
328
329
330 public boolean hasEntry( EntryOperationContext opContext ) throws Exception
331 {
332 return null != getEntryId( opContext.getDn().getNormName() );
333 }
334
335
336 public abstract void rename( RenameOperationContext opContext ) throws Exception;
337
338
339 public abstract void move( MoveOperationContext opContext ) throws Exception;
340
341
342 public abstract void moveAndRename( MoveAndRenameOperationContext opContext ) throws Exception;
343
344
345 public abstract void sync() throws Exception;
346
347
348 ////////////////////
349 // public abstract methods
350
351 // ------------------------------------------------------------------------
352 // Index Operations
353 // ------------------------------------------------------------------------
354
355 public abstract void addIndexOn( Index<? extends Object, ServerEntry, ID> index ) throws Exception;
356
357
358 public abstract boolean hasUserIndexOn( String attribute ) throws Exception;
359
360
361 public abstract boolean hasSystemIndexOn( String attribute ) throws Exception;
362
363
364 public abstract Index<String, ServerEntry, ID> getPresenceIndex();
365
366
367 /**
368 * Gets the Index mapping the primary keys of parents to the
369 * primary keys of their children.
370 *
371 * @return the one level Index
372 */
373 public abstract Index<ID, ServerEntry, ID> getOneLevelIndex();
374
375
376 /**
377 * Gets the Index mapping the primary keys of ancestors to the
378 * primary keys of their descendants.
379 *
380 * @return the sub tree level Index
381 */
382 public abstract Index<ID, ServerEntry, ID> getSubLevelIndex();
383
384
385 /**
386 * Gets the Index mapping user provided distinguished names of entries as
387 * Strings to the BigInteger primary keys of entries.
388 *
389 * @return the user provided distinguished name Index
390 */
391 public abstract Index<String, ServerEntry, ID> getUpdnIndex();
392
393
394 /**
395 * Gets the Index mapping the normalized distinguished names of entries as
396 * Strings to the BigInteger primary keys of entries.
397 *
398 * @return the normalized distinguished name Index
399 */
400 public abstract Index<String, ServerEntry, ID> getNdnIndex();
401
402
403 /**
404 * Gets the alias index mapping parent entries with scope expanding aliases
405 * children one level below them; this system index is used to dereference
406 * aliases on one/single level scoped searches.
407 *
408 * @return the one alias index
409 */
410 public abstract Index<ID, ServerEntry, ID> getOneAliasIndex();
411
412
413 /**
414 * Gets the alias index mapping relative entries with scope expanding
415 * alias descendents; this system index is used to dereference aliases on
416 * subtree scoped searches.
417 *
418 * @return the sub alias index
419 */
420 public abstract Index<ID, ServerEntry, ID> getSubAliasIndex();
421
422
423 /**
424 * Gets the system index defined on the ALIAS_ATTRIBUTE which for LDAP would
425 * be the aliasedObjectName and for X.500 would be aliasedEntryName.
426 *
427 * @return the index on the ALIAS_ATTRIBUTE
428 */
429 public abstract Index<String, ServerEntry, ID> getAliasIndex();
430
431
432 /**
433 * Sets the system index defined on the ALIAS_ATTRIBUTE which for LDAP would
434 * be the aliasedObjectName and for X.500 would be aliasedEntryName.
435 *
436 * @org.apache.xbean.Property hidden="true"
437 * @param index the index on the ALIAS_ATTRIBUTE
438 * @throws Exception if there is a problem setting up the index
439 */
440 public abstract void setAliasIndexOn( Index<String, ServerEntry, ID> index ) throws Exception;
441
442
443 /**
444 * Sets the attribute existence Index.
445 *
446 * @org.apache.xbean.Property hidden="true"
447 * @param index the attribute existence Index
448 * @throws Exception if there is a problem setting up the index
449 */
450 public abstract void setPresenceIndexOn( Index<String, ServerEntry, ID> index ) throws Exception;
451
452
453 /**
454 * Sets the one level Index.
455 *
456 * @org.apache.xbean.Property hidden="true"
457 * @param index the one level Index
458 * @throws Exception if there is a problem setting up the index
459 */
460 public abstract void setOneLevelIndexOn( Index<ID, ServerEntry, ID> index ) throws Exception;
461
462
463 // TODO - add sub level index setter
464
465 /**
466 * Sets the user provided distinguished name Index.
467 *
468 * @org.apache.xbean.Property hidden="true"
469 * @param index the updn Index
470 * @throws Exception if there is a problem setting up the index
471 */
472 public abstract void setUpdnIndexOn( Index<String, ServerEntry, ID> index ) throws Exception;
473
474
475 /**
476 * Sets the normalized distinguished name Index.
477 *
478 * @org.apache.xbean.Property hidden="true"
479 * @param index the ndn Index
480 * @throws Exception if there is a problem setting up the index
481 */
482 public abstract void setNdnIndexOn( Index<String, ServerEntry, ID> index ) throws Exception;
483
484
485 /**
486 * Sets the alias index mapping parent entries with scope expanding aliases
487 * children one level below them; this system index is used to dereference
488 * aliases on one/single level scoped searches.
489 *
490 * @org.apache.xbean.Property hidden="true"
491 * @param index a one level alias index
492 * @throws Exception if there is a problem setting up the index
493 */
494 public abstract void setOneAliasIndexOn( Index<ID, ServerEntry, ID> index ) throws Exception;
495
496
497 /**
498 * Sets the alias index mapping relative entries with scope expanding
499 * alias descendents; this system index is used to dereference aliases on
500 * subtree scoped searches.
501 *
502 * @org.apache.xbean.Property hidden="true"
503 * @param index a subtree alias index
504 * @throws Exception if there is a problem setting up the index
505 */
506 public abstract void setSubAliasIndexOn( Index<ID, ServerEntry, ID> index ) throws Exception;
507
508
509 /**
510 * {@inheritDoc}
511 */
512 public void setSuffix( String suffix ) throws LdapInvalidDnException
513 {
514 this.suffix = new DN( suffix );
515 }
516
517
518 /**
519 * {@inheritDoc}
520 */
521 public String getSuffix()
522 {
523 return suffix.getName();
524 }
525
526
527 /**
528 * {@inheritDoc}
529 */
530 public DN getSuffixDn()
531 {
532 return suffix;
533 }
534
535
536 public abstract Index<? extends Object, ServerEntry, ID> getUserIndex( String attribute ) throws Exception;
537
538
539 public abstract Index<? extends Object, ServerEntry, ID> getSystemIndex( String attribute ) throws Exception;
540
541
542 public abstract ID getEntryId( String dn ) throws Exception;
543
544
545 public abstract String getEntryDn( ID id ) throws Exception;
546
547
548 public abstract ID getParentId( String dn ) throws Exception;
549
550
551 public abstract ID getParentId( ID childId ) throws Exception;
552
553
554 /**
555 * Gets the user provided distinguished name.
556 *
557 * @param id the entry id
558 * @return the user provided distinguished name
559 * @throws Exception if the updn index cannot be accessed
560 */
561 public abstract String getEntryUpdn( ID id ) throws Exception;
562
563
564 /**
565 * Gets the user provided distinguished name.
566 *
567 * @param dn the normalized distinguished name
568 * @return the user provided distinguished name
569 * @throws Exception if the updn and ndn indices cannot be accessed
570 */
571 public abstract String getEntryUpdn( String dn ) throws Exception;
572
573
574 public abstract ClonedServerEntry lookup( ID id ) throws Exception;
575
576
577 public abstract void delete( ID id ) throws Exception;
578
579
580 public abstract IndexCursor<ID, ServerEntry, ID> list( ID id ) throws Exception;
581
582
583 public abstract int getChildCount( ID id ) throws Exception;
584
585
586 public abstract void setProperty( String key, String value ) throws Exception;
587
588
589 public abstract String getProperty( String key ) throws Exception;
590
591
592 public abstract Iterator<String> getUserIndices();
593
594
595 public abstract Iterator<String> getSystemIndices();
596
597
598 /**
599 * Gets the count of the total number of entries in the database.
600 *
601 * TODO shouldn't this be a BigInteger instead of an int?
602 *
603 * @return the number of entries in the database
604 * @throws Exception if there is a failure to read the count
605 */
606 public abstract int count() throws Exception;
607 }