| | |
| | | */ |
| | | package org.opends.server.backends.pluggable; |
| | | |
| | | import static org.forgerock.util.Utils.closeSilently; |
| | | import static org.opends.messages.BackendMessages.*; |
| | | import static org.opends.server.util.StaticUtils.*; |
| | | |
| | |
| | | */ |
| | | void open(final AccessMode accessMode) throws StorageRuntimeException, ConfigException |
| | | { |
| | | boolean opened = false; |
| | | boolean storageOpened = false; |
| | | try |
| | | { |
| | | storage.open(accessMode); |
| | | storageOpened = true; |
| | | storage.write(new WriteOperation() |
| | | { |
| | | @Override |
| | |
| | | // after the write, never inside it: a compressed schema migration is only worth reporting |
| | | // once the transaction that copied it has committed, and a replayed operation runs twice |
| | | compressedSchema.reportMigration(); |
| | | opened = true; |
| | | } |
| | | catch(StorageRuntimeException e) |
| | | { |
| | |
| | | { |
| | | throw new StorageRuntimeException(e); |
| | | } |
| | | finally |
| | | { |
| | | if (!opened) |
| | | { |
| | | giveUpAfterFailedOpen(storageOpened); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Gives back what a root container which failed to open took. Nothing else will: the caller |
| | | * throws it away - {@link BackendImpl#newRootContainer} lets every failure through without a |
| | | * reference to it left anywhere - and {@code BackendConfigManager} releases the backend's shared |
| | | * lock without calling {@code closeBackend()} for a backend which never opened. What is left here |
| | | * is left for the life of the JVM: entry containers and this root container go on answering the |
| | | * configuration changes of a backend which is not running, and every later attempt to enable that |
| | | * backend adds another set of them. |
| | | * <p> |
| | | * The failure being given up after is the one worth reporting, so nothing here is allowed to |
| | | * replace it. |
| | | * |
| | | * @param storageOpened whether the storage opened, which is false on one road only: its |
| | | * {@code open()} threw. A storage whose open failed is not one this container can |
| | | * close - what that open took before it failed is the storage's own to give back, as |
| | | * {@code PDBStorage}, {@code JEStorage} and {@code JDBCStorage} do - and there is no other: every |
| | | * root container is opened over a storage no root container holds, since |
| | | * {@code BackendImpl} opens one, read only or not, only while it has none. |
| | | */ |
| | | private void giveUpAfterFailedOpen(boolean storageOpened) |
| | | { |
| | | try |
| | | { |
| | | for (DN baseDN : entryContainers.keySet()) |
| | | { |
| | | closeSilently(unregisterEntryContainer(baseDN)); |
| | | } |
| | | config.removePluggableChangeListener(this); |
| | | if (storageOpened) |
| | | { |
| | | storage.close(); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | private void openAndRegisterEntryContainers(WriteableTransaction txn, Set<DN> baseDNs, AccessMode accessMode) |
| | | throws StorageRuntimeException, InitializationException, ConfigException |
| | | { |
| | | // Give up what a previous, rolled back attempt registered: this runs inside the write |
| | | // Storage.write may replay, and an entry container left registered fails the attempt which |
| | | // replaces it with ERR_ENTRY_CONTAINER_ALREADY_REGISTERED - so a write-write conflict, which |
| | | // the replay is there to absorb, would leave the backend unopened instead - while keeping the |
| | | // configuration listeners it opened with, which only its close() takes back. The same shape as |
| | | // BackendImpl.changeBaseDNTrees, which opens its entry containers inside a write for the same |
| | | // reason. Nothing else can reach these containers: the backend registers its base DNs only once |
| | | // this has returned, so they are closed without being locked. |
| | | for (DN baseDN : baseDNs) |
| | | { |
| | | closeSilently(unregisterEntryContainer(baseDN)); |
| | | } |
| | | |
| | | EntryID highestID = null; |
| | | for (DN baseDN : baseDNs) |
| | | { |
| | | EntryContainer ec = openEntryContainer(baseDN, txn, accessMode); |
| | | EntryID id = ec.getHighestEntryID(txn); |
| | | // Registered before anything else here can throw: a container which opened has registered |
| | | // every listener it ever will, and only what this map holds is given back when the open of |
| | | // the root container fails. |
| | | registerEntryContainer(baseDN, ec); |
| | | EntryID id = ec.getHighestEntryID(txn); |
| | | if (highestID == null || id.compareTo(highestID) > 0) |
| | | { |
| | | highestID = id; |