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 }