View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  package org.apache.directory.server.xdbm;
21  
22  
23  import java.net.URI;
24  import java.util.Arrays;
25  import java.util.Collections;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.concurrent.locks.ReadWriteLock;
32  
33  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
34  import org.apache.directory.api.ldap.model.entry.Entry;
35  import org.apache.directory.api.ldap.model.entry.Modification;
36  import org.apache.directory.api.ldap.model.exception.LdapException;
37  import org.apache.directory.api.ldap.model.name.Dn;
38  import org.apache.directory.api.ldap.model.name.Rdn;
39  import org.apache.directory.api.ldap.model.schema.AttributeType;
40  import org.apache.directory.server.constants.ApacheSchemaConstants;
41  import org.apache.directory.server.core.api.interceptor.context.ModDnAva;
42  import org.apache.directory.server.core.api.partition.PartitionTxn;
43  
44  import com.github.benmanes.caffeine.cache.Cache;
45  
46  
47  /**
48   * Represents an entry store based on the Table, Index, and MasterTable
49   * database structure.
50   *
51   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
52   */
53  public interface Store
54  {
55      /*
56       * W H Y   H A V E   A   S T O R E   I N T E R F A C E  ?
57       * ------------------------------------------------------
58       *
59       * Some may question why we have this Store interface when the Partition
60       * interface abstracts away partition implementation details in the server
61       * core.  This is due to a complicated chicken and egg problem with the
62       * additional need to abstract stores for the SearchEngine.  This way the
63       * SearchEngine and it's default implementation can be independent of the
64       * Partition interface.  Once this is achieved the default SearchEngine
65       * implementation can be removed from the core.  This will allow for
66       * better modularization, with the ability to easily substitute new
67       * SearchEngine implementations into ApacheDS.
68       *
69       *
70       * H I S T O R Y
71       * -------------
72       *
73       * Originally the JdbmStore class came about due to a cyclic dependency.
74       * The bootstrap-partition module is created by the bootstrap-plugin
75       * module.  The core depends on the bootstrap-partition module to
76       * bootstrap the server.  The bootstrap-partition module depends on the
77       * bootstrap-plugin which builds a JdbmStore stuffing it with all the
78       * information needed for the server to bootstrap.  The bootstrap-plugin
79       * hence must be built before it can generate the bootstrap-partition and
80       * it cannot have a dependency on the core.  We could not use the
81       * JdbmPartition because it depends on the Partition interface and this
82       * is an integral part of the core.  If we did then there would be a
83       * cyclic dependency between modules in the apacheds pom.  To avoid this
84       * the JdbmStore class was created and the guts of the JDBM partition were
85       * put into the jdbm-store module.  This jdbm-store module does not depend
86       * on core and can be used by the bootstrap-plugin to build the
87       * bootstrap-partition.
88       *
89       * Hence it's project dependencies that drove the creation of the
90       * JdbmStore class.  Later we realized, the default SeachEngine used by
91       * all Table, Index, MasterTable scheme based partitions depends on
92       * BTreePartition which depends on Partition.  We would like to remove
93       * this search engine out of the core so it can easily be swapped out,
94       * but most importantly so we can have the search depend on any kind of
95       * store.  There's no reason why the SearchEngine should depend on a
96       * Partition (store with search capabilities) when it just needs a simple
97       * store and it's indices to conduct search operations.
98       */
99      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 }