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    }