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 */
020package org.apache.directory.server.xdbm;
021
022
023import java.net.URI;
024import java.util.Arrays;
025import java.util.Collections;
026import java.util.HashSet;
027import java.util.Iterator;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031import java.util.concurrent.locks.ReadWriteLock;
032
033import org.apache.directory.api.ldap.model.constants.SchemaConstants;
034import org.apache.directory.api.ldap.model.entry.Entry;
035import org.apache.directory.api.ldap.model.entry.Modification;
036import org.apache.directory.api.ldap.model.exception.LdapException;
037import org.apache.directory.api.ldap.model.name.Dn;
038import org.apache.directory.api.ldap.model.name.Rdn;
039import org.apache.directory.api.ldap.model.schema.AttributeType;
040import org.apache.directory.server.constants.ApacheSchemaConstants;
041import org.apache.directory.server.core.api.interceptor.context.ModDnAva;
042import org.apache.directory.server.core.api.partition.PartitionTxn;
043
044import com.github.benmanes.caffeine.cache.Cache;
045
046
047/**
048 * Represents an entry store based on the Table, Index, and MasterTable
049 * database structure.
050 *
051 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
052 */
053public interface Store
054{
055    /*
056     * W H Y   H A V E   A   S T O R E   I N T E R F A C E  ?
057     * ------------------------------------------------------
058     *
059     * Some may question why we have this Store interface when the Partition
060     * interface abstracts away partition implementation details in the server
061     * core.  This is due to a complicated chicken and egg problem with the
062     * additional need to abstract stores for the SearchEngine.  This way the
063     * SearchEngine and it's default implementation can be independent of the
064     * Partition interface.  Once this is achieved the default SearchEngine
065     * implementation can be removed from the core.  This will allow for
066     * better modularization, with the ability to easily substitute new
067     * SearchEngine implementations into ApacheDS.
068     *
069     *
070     * H I S T O R Y
071     * -------------
072     *
073     * Originally the JdbmStore class came about due to a cyclic dependency.
074     * The bootstrap-partition module is created by the bootstrap-plugin
075     * module.  The core depends on the bootstrap-partition module to
076     * bootstrap the server.  The bootstrap-partition module depends on the
077     * bootstrap-plugin which builds a JdbmStore stuffing it with all the
078     * information needed for the server to bootstrap.  The bootstrap-plugin
079     * hence must be built before it can generate the bootstrap-partition and
080     * it cannot have a dependency on the core.  We could not use the
081     * JdbmPartition because it depends on the Partition interface and this
082     * is an integral part of the core.  If we did then there would be a
083     * cyclic dependency between modules in the apacheds pom.  To avoid this
084     * the JdbmStore class was created and the guts of the JDBM partition were
085     * put into the jdbm-store module.  This jdbm-store module does not depend
086     * on core and can be used by the bootstrap-plugin to build the
087     * bootstrap-partition.
088     *
089     * Hence it's project dependencies that drove the creation of the
090     * JdbmStore class.  Later we realized, the default SeachEngine used by
091     * all Table, Index, MasterTable scheme based partitions depends on
092     * BTreePartition which depends on Partition.  We would like to remove
093     * this search engine out of the core so it can easily be swapped out,
094     * but most importantly so we can have the search depend on any kind of
095     * store.  There's no reason why the SearchEngine should depend on a
096     * Partition (store with search capabilities) when it just needs a simple
097     * store and it's indices to conduct search operations.
098     */
099    String[] SYS_INDEX_OID_ARRAY =
100        {
101            ApacheSchemaConstants.APACHE_PRESENCE_AT_OID,
102            ApacheSchemaConstants.APACHE_RDN_AT_OID,
103            ApacheSchemaConstants.APACHE_ALIAS_AT_OID,
104            ApacheSchemaConstants.APACHE_ONE_ALIAS_AT_OID,
105            ApacheSchemaConstants.APACHE_SUB_ALIAS_AT_OID,
106            SchemaConstants.ENTRY_CSN_AT_OID,
107            SchemaConstants.OBJECT_CLASS_AT_OID,
108            SchemaConstants.ADMINISTRATIVE_ROLE_AT_OID
109    };
110
111    Set<String> SYS_INDEX_OIDS = Collections.unmodifiableSet( new HashSet<String>( Arrays
112        .asList( SYS_INDEX_OID_ARRAY ) ) );
113
114
115    /**
116     * Sets the partition path (working directory) for the store.
117     * 
118     * @param partitionPath the new partition path
119     */
120    void setPartitionPath( URI partitionPath );
121
122
123    /**
124     * Gets the partition path (working directory) for the store.
125     * 
126     * @return The current partition path (working directory) for the store
127     */
128    URI getPartitionPath();
129
130
131    /**
132     * Sets the flag telling the server to flush on disk when some
133     * modification has been done.
134     * @param isSyncOnWrite A boolean set to true if we have to flush on disk
135     * when a modification occurs
136     */
137    void setSyncOnWrite( boolean isSyncOnWrite );
138
139
140    /**
141     * @return <code>true</code> if we write to disk for every modification
142     */
143    boolean isSyncOnWrite();
144
145
146    /**
147     * Sets the cache size for this store.
148     * @param cacheSize The cache size
149     */
150    void setCacheSize( int cacheSize );
151
152
153    /**
154     * Gets the cache size for this store.
155     * 
156     * @return The cache size
157     */
158    int getCacheSize();
159
160
161    /**
162     * Adds a (system or user) index to the list of index for this store.
163     * Note that the attribute id returned by Index.getAttributeId() must be
164     * the numeric OID.
165     * @param index The index to add
166     * @throws Exception If the addition failed
167     */
168    void addIndex( Index<?, String> index ) throws Exception;
169
170
171    //------------------------------------------------------------------------
172    // System index
173    //------------------------------------------------------------------------
174    /**
175     * @return The Presence system index
176     */
177    Index<String, String> getPresenceIndex();
178
179
180    /**
181     * @return The Alias system index
182     */
183    Index<Dn, String> getAliasIndex();
184
185
186    /**
187     * @return The OneAlias system index
188     */
189    Index<String, String> getOneAliasIndex();
190
191
192    /**
193     * @return The SubAlias system index
194     */
195    Index<String, String> getSubAliasIndex();
196
197
198    /**
199     * Retrieve the SuffixID
200     * 
201     * @param partitionTxn The transaction to use
202     * @return The suddix ID
203     * @throws LdapException If we can't get the suffix ID
204     */
205    String getSuffixId( PartitionTxn partitionTxn ) throws LdapException;
206
207
208    /**
209     * @return The Rdn system index
210     */
211    Index<ParentIdAndRdn, String> getRdnIndex();
212
213
214    /**
215     * @return The ObjectClass system index
216     */
217    Index<String, String> getObjectClassIndex();
218
219
220    /**
221     * @return The EntryCSN system index
222     */
223    Index<String, String> getEntryCsnIndex();
224
225
226    /**
227     * @return An iterator build on top of the User's index
228     */
229    Iterator<String> getUserIndices();
230
231
232    /**
233     * @return An iterator build on top of the System's index
234     */
235    Iterator<String> getSystemIndices();
236
237
238    /**
239     * Tells if an index is already present in the User's <strong>or</strong> System's index list
240     * 
241     * @param attributeType The attributeType we are looking for
242     * @return <code>true</code> if the index is already present in the
243     * User's <strong>or</strong> System's index list
244     * @throws LdapException If something went wrong
245     */
246    boolean hasIndexOn( AttributeType attributeType ) throws LdapException;
247
248
249    /**
250     * Tells if an index is already present in the User's index list
251     * 
252     * @param attributeType The attributeType index we are looking for
253     * @return <code>true</code> if the index is already present in the
254     * User's index list
255     * @throws LdapException If something went wrong
256     */
257    boolean hasUserIndexOn( AttributeType attributeType ) throws LdapException;
258
259
260    /**
261     * Tells if an index is already present in the System's index list
262     * @param attributeType The index we are looking for
263     * @return <code>true</code> if the index is already present in the
264     * System's index list
265     * @throws LdapException If something went wrong
266     */
267    boolean hasSystemIndexOn( AttributeType attributeType ) throws LdapException;
268
269
270    /**
271     * Get the user <strong>or</strong> system index associated with the given attributeType
272     * 
273     * @param attributeType The index attributeType we are looking for
274     * @return The associated user <strong>or</strong> system index
275     * @throws IndexNotFoundException If the index does not exist
276     */
277    Index<?, String> getIndex( AttributeType attributeType ) throws IndexNotFoundException;
278
279
280    /**
281     * Get the user index associated with the given name
282     * @param attributeType The index name we are looking for
283     * @return The associated user index
284     * @throws IndexNotFoundException If the index does not exist
285     */
286    Index<?, String> getUserIndex( AttributeType attributeType ) throws IndexNotFoundException;
287
288
289    /**
290     * Get the system index associated with the given name
291     * @param attributeType The index name we are looking for
292     * @return The associated system index
293     * @throws IndexNotFoundException If the index does not exist
294     */
295    Index<?, String> getSystemIndex( AttributeType attributeType ) throws IndexNotFoundException;
296
297
298    /**
299     * Gets the entry's id. Returns <code>null</code> if the Dn doesn't exist in this store.
300     * Note that the Dn must be normalized!
301     * 
302     * @param partitionTxn The transaction to use
303     * @param dn the normalized entry Dn
304     * @return the entry's id, or <code>null</code> if the Dn doesn't exists
305     * @throws LdapException If we can't get the entry ID
306     */
307    String getEntryId( PartitionTxn partitionTxn, Dn dn ) throws LdapException;
308
309
310    /**
311     * Gets the Entry's Dn identified by the given id.
312     * 
313     * @param partitionTxn The transaction to use
314     * @param id the entry's id
315     * @return the entry's Dn
316     * @throws LdapException If we can't get the entry Dn
317     */
318    Dn getEntryDn( PartitionTxn partitionTxn, String id ) throws LdapException;
319
320
321    /**
322     * Gets the UUID of an entry's parent using the child entry's UUID.
323     * Note that the suffix entry returns 0, which does not map to any entry.
324     *
325     * @param partitionTxn The transaction to use
326     * @param childId the UUID of the entry
327     * @return the id of the parent entry or zero if the suffix entry UUID is used
328     * @throws LdapException on failures to access the underlying store
329     */
330    String getParentId( PartitionTxn partitionTxn, String childId ) throws LdapException;
331
332
333    /**
334     * Gets the total count of entries within this store.
335     *
336     * @param partitionTxn The transaction to use
337     * @return the total count of entries within this store
338     * @throws LdapException on failures to access the underlying store
339     */
340    long count( PartitionTxn partitionTxn ) throws LdapException;
341
342
343    /**
344     * Delete an entry from the store
345     *
346     * @param partitionTxn The transaction to use
347     * @param id The Entry UUID we want to delete
348     * @return the deleted entry if found
349     * @throws LdapException If the deletion failed for any reason
350     */
351    Entry delete( PartitionTxn partitionTxn, String id ) throws LdapException;
352
353
354    /**
355     * Get back an entry knowing its UUID
356     *
357     * @param partitionTxn The transaction to use
358     * @param id The Entry UUID we want to get back
359     * @return The found Entry, or null if not found
360     * @throws LdapException If the lookup failed for any reason (except a not found entry)
361     */
362    Entry fetch( PartitionTxn partitionTxn, String id ) throws LdapException;
363
364
365    /**
366     * Get back an entry knowing its UUID
367     *
368     * @param partitionTxn The transaction to use
369     * @param id The Entry UUID we want to get back
370     * @param dn The entry DN when we have it
371     * @return The found Entry, or null if not found
372     * @throws LdapException If the lookup failed for any reason (except a not found entry)
373     */
374    Entry fetch( PartitionTxn partitionTxn, String id, Dn dn ) throws LdapException;
375
376
377    /**
378     * Gets the count of immediate children of the given entry UUID.
379     *
380     * @param partitionTxn The transaction to use
381     * @param id the entry UUID
382     * @return the child count
383     * @throws LdapException on failures to access the underlying store
384     */
385    long getChildCount( PartitionTxn partitionTxn, String id ) throws LdapException;
386
387
388    /**
389     * Modify an entry applying the given list of modifications.
390     *
391     * @param partitionTxn The transaction to use
392     * @param dn The Entry's Dn
393     * @param mods The list of modifications
394     * @return The modified entry
395     * @throws LdapException If the modification failed
396     */
397    Entry modify( PartitionTxn partitionTxn, Dn dn, Modification... mods ) throws LdapException;
398
399
400    /**
401     * Changes the relative distinguished name of an entry specified by a
402     * distinguished name with the optional removal of the old Rdn attribute
403     * value from the entry.  Name changes propagate down as dn changes to the
404     * descendants of the entry where the Rdn changed.
405     *
406     * An Rdn change operation does not change parent child relationships.  It
407     * merely propagates a name change at a point in the DIT where the Rdn is
408     * changed. The change propagates down the subtree rooted at the
409     * distinguished name specified.
410     *
411     * @param partitionTxn The transaction to use
412     * @param dn the normalized distinguished name of the entry to alter
413     * @param newRdn the new Rdn to set
414     * @param deleteOldRdn whether or not to remove the old Rdn attr/val
415     * @param entry the modified entry
416     * @throws LdapException if there are any errors propagating the name changes
417     */
418    void rename( PartitionTxn partitionTxn, Dn dn, Rdn newRdn, boolean deleteOldRdn, Entry entry ) throws LdapException;
419
420
421    /**
422     * Move and Rename operation. The entry is moved from one part of the DIT to another part of 
423     * the DIT. Its RDN is also changed in the process.
424     * 
425     * @param partitionTxn The transaction to use
426     * @param oldDn The previous DN
427     * @param newSuperiorDn The previous parent's DN
428     * @param newRdn The new DN
429     * @param modAvas The changed Attributes caused by the renaming (added and removed attributes)
430     * @param modifiedEntry the entry to move
431     * @throws LdapException If the modification failed
432     */
433    void moveAndRename( PartitionTxn partitionTxn, Dn oldDn, Dn newSuperiorDn, Rdn newRdn, Map<String, List<ModDnAva>> modAvas, 
434        Entry modifiedEntry ) throws LdapException;
435
436
437    /**
438     * <p>Move an entry from one place to the other. The Rdn remains unchanged,
439     * the parent Dn changes</p>
440     * <p>We have to update some of the index when moving an entry. Assuming
441     * that the target destination does not exist, the following index must
442     * be updated :</p>
443     * <ul>
444     * <li><b>oneLevel</b> index</li>
445     * <li><b>subLevel</b> index</li>
446     * </ul>
447     * <p>If the moved entry is an alias, then we also have to update the
448     * following index :</p>
449     * <ul>
450     * <li><b>oneAlias</b> index</li>
451     * <li><b>subAlias</b> index</li>
452     * </ul>
453     * <p>The <b>Alias</b> index is not updated, as the entry UUID won't change.</p>
454     * <p>We have a few check we must do before moving the entry :
455     * <ul>
456     * <li>The destination must not exist
457     * <li>The moved entry must exist (this has already been checked)
458     * <li>The moved entry must not inherit from a referral (already checked)
459     * </ul>
460     *
461     * @param partitionTxn The transaction to use
462     * @param oldDn The previous entry Dn
463     * @param newSuperior The new superior Dn
464     * @param newDn The new Dn
465     * @param entry The entry to move
466     * @throws LdapException If the move failed
467     */
468    void move( PartitionTxn partitionTxn, Dn oldDn, Dn newSuperior, Dn newDn, Entry entry ) throws LdapException;
469
470
471    /**
472     * Expose the Master table
473     * @return The masterTable instance
474     */
475    MasterTable getMasterTable();
476
477
478    /**
479     * @return The ReadWrite lock used to protect the server against concurrent read and writes
480     */
481    ReadWriteLock getReadWriteLock();
482    
483    
484    /**
485     * @return the Alias cache
486     * @return The cache
487     */
488    Cache< String, Dn > getAliasCache();
489}