LDIFBackend.java revision 763a75aeed1a7731ddb95b99496aa7c1bf206ed0
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2007-2008 Sun Microsystems, Inc.
* Portions Copyright 2011-2015 ForgeRock AS
*/
/**
* This class provides a backend implementation that stores the underlying data
* in an LDIF file. When the backend is initialized, the contents of the
* backend are read into memory and all read operations are performed purely
* from memory. Write operations cause the underlying LDIF file to be
* re-written on disk.
*/
public class LDIFBackend
extends Backend<LDIFBackendCfg>
{
/** The base DNs for this backend. */
/** The mapping between parent DNs and their immediate children. */
/** The base DNs for this backend, in a hash set. */
/** The set of supported controls for this backend. */
/** The current configuration for this backend. */
private LDIFBackendCfg currentConfig;
/** The mapping between entry DNs and the corresponding entries. */
/** A read-write lock used to protect access to this backend. */
private final ReentrantReadWriteLock backendLock;
/** The path to the LDIF file containing the data for this backend. */
private String ldifFilePath;
/**
* Creates a new backend with the provided information. All backend
* implementations must implement a default constructor that use
* <CODE>super()</CODE> to invoke this constructor.
*/
public LDIFBackend()
{
super();
boolean useFairLocking =
}
/** {@inheritDoc} */
public void initializeBackend()
{
// We won't support anything other than exactly one base DN in this
// implementation. If we were to add such support in the future, we would
// likely want to separate the data for each base DN into a separate entry
// map.
{
}
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
dn, getExceptionMessage(e));
throw new InitializationException(message, e);
}
}
readLDIF();
}
/**
* Reads the contents of the LDIF backing file into memory.
*
* @throws InitializationException If a problem occurs while reading the
* LDIF file.
*/
private void readLDIF()
throws InitializationException
{
{
// This is fine. We will just start with an empty backend.
if (logger.isTraceEnabled())
{
ldifFilePath + " does not exist");
}
return;
}
try
{
}
catch (DirectoryException de)
{
}
}
/**
* Writes the current set of entries to the target LDIF file. The new LDIF
* will first be created as a temporary file and then renamed into place. The
* caller must either hold the write lock for this backend, or must ensure
* that it's in some other state that guarantees exclusive access to the data.
*
* @throws DirectoryException If a problem occurs that prevents the updated
* LDIF from being written.
*/
private void writeLDIF()
throws DirectoryException
{
// Write the new data to a temporary file.
try
{
}
catch (Exception e)
{
logger.traceException(e);
currentConfig.dn(),
m, e);
}
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
currentConfig.dn(),
m, e);
}
}
// Rename the existing "live" file out of the way and move the new file
// into place.
try
{
{
}
}
catch (Exception e)
{
logger.traceException(e);
}
try
{
{
}
}
catch (Exception e)
{
logger.traceException(e);
}
try
{
}
catch (Exception e)
{
logger.traceException(e);
currentConfig.dn(),
m, e);
}
}
/** {@inheritDoc} */
public void finalizeBackend()
{
try
{
{
try
{
}
catch (Exception e)
{
logger.traceException(e);
}
}
}
finally
{
}
}
/** {@inheritDoc} */
public DN[] getBaseDNs()
{
return baseDNs;
}
/** {@inheritDoc} */
public long getEntryCount()
{
try
{
{
}
return -1;
}
finally
{
}
}
/** {@inheritDoc} */
{
// All searches in this backend will always be considered indexed.
return true;
}
/** {@inheritDoc} */
throws DirectoryException
{
try
{
{
// It could be that the entry doesn't exist, in which case we should
// throw an exception.
{
return ConditionResult.FALSE;
}
else
{
}
}
else
{
return ConditionResult.TRUE;
}
}
finally
{
}
}
/** {@inheritDoc} */
throws DirectoryException
{
try
{
{
// It could be that the entry doesn't exist, in which case we should
// throw an exception.
{
return 0L;
}
else
{
}
}
else
{
if(!subtree)
{
return childDNSet.size();
}
else
{
long count = 0;
{
count ++;
}
return count;
}
}
}
finally
{
}
}
/** {@inheritDoc} */
{
try
{
}
finally
{
}
}
/** {@inheritDoc} */
{
try
{
}
finally
{
}
}
/** {@inheritDoc} */
throws DirectoryException
{
try
{
// Make sure that the target entry does not already exist, but that its
// parent does exist (or that the entry being added is the base DN).
{
}
{
writeLDIF();
return;
}
else
{
{
if (childDNSet == null)
{
}
writeLDIF();
return;
}
else
{
{
while (true)
{
{
break;
}
{
break;
}
}
}
}
}
}
finally
{
}
}
/** {@inheritDoc} */
throws DirectoryException
{
try
{
// Get the DN of the target entry's parent, if it exists. We'll need to
// also remove the reference to the target entry from the parent's set of
// children.
// Make sure that the target entry exists. If not, then fail.
{
{
{
break;
}
}
}
// See if the target entry has any children. If so, then we'll only
// delete it if the request contains the subtree delete control (in
// which case we'll delete the entire subtree).
{
{
if (parentChildren != null)
{
if (parentChildren.isEmpty())
{
}
}
}
}
else
{
if (! subtreeDelete)
{
}
{
if (parentChildren != null)
{
if (parentChildren.isEmpty())
{
}
}
}
{
}
}
writeLDIF();
}
finally
{
}
}
/**
* Removes the specified entry and any subordinates that it may have from
* the backend. This method assumes that the caller holds the backend write
* lock.
*
* @param entryDN The DN of the entry to remove, along with all of its
* subordinate entries.
*/
{
if (childDNSet != null)
{
{
}
}
}
/** {@inheritDoc} */
{
try
{
// Make sure that the target entry exists. If not, then fail.
{
{
{
break;
}
}
}
writeLDIF();
return;
}
finally
{
}
}
/** {@inheritDoc} */
throws DirectoryException
{
try
{
// Make sure that the original entry exists and that the new entry doesn't
// exist but its parent does.
{
{
{
break;
}
}
}
{
}
{
}
// Remove the entry from the list of children for the old parent and
// add the new entry DN to the set of children for the new parent.
if (parentChildDNs != null)
{
if (parentChildDNs.isEmpty()
{
}
}
if (parentChildDNs == null)
{
}
// If the entry has children, then we'll need to work on the whole
// subtree. Otherwise, just work on the target entry.
{
{
}
}
writeLDIF();
}
finally
{
}
}
/**
* Moves the specified entry and all of its children so that they are
* appropriately placed below the given new parent DN. This method assumes
* that the caller holds the backend write lock.
*
* @param newParentDN The DN of the new parent under which the entry should
* be placed.
*/
{
{
// This should never happen.
if (logger.isTraceEnabled())
{
entryDN + " for nonexistent entry.");
}
return;
}
if (parentChildren == null)
{
}
if (childDNSet != null)
{
{
}
}
}
/** {@inheritDoc} */
throws DirectoryException
{
try
{
// Get the base DN, scope, and filter for the search.
// Make sure the base entry exists if it's supposed to be in this backend.
{
{
{
break;
}
}
throw new DirectoryException(
}
{
}
// If it's a base-level search, then just get that entry and return it if
// it matches the filter.
{
{
}
}
else
{
// Walk through all entries and send the ones that match.
{
e = e.duplicate(true);
{
}
}
}
}
finally
{
}
}
/** {@inheritDoc} */
{
return supportedControls;
}
/** {@inheritDoc} */
{
return Collections.emptySet();
}
/** {@inheritDoc} */
{
switch (backendOperation)
{
case LDIF_EXPORT:
case LDIF_IMPORT:
return true;
default:
return false;
}
}
/** {@inheritDoc} */
throws DirectoryException
{
try
{
// Create the LDIF writer.
try
{
}
catch (Exception e)
{
logger.traceException(e);
m, e);
}
// Walk through all the entries and write them to LDIF.
try
{
{
}
}
catch (Exception e)
{
m, e);
}
finally
{
}
}
finally
{
}
}
/** {@inheritDoc} */
throws DirectoryException
{
return importLDIF(importConfig, true);
}
/**
* Processes an LDIF import operation, optionally writing the resulting LDIF
* to disk.
*
* @param importConfig The LDIF import configuration.
* @param writeLDIF Indicates whether the LDIF backing file for this
* backend should be updated when the import is
* complete. This should only be {@code false} when
* reading the LDIF as the backend is coming online.
*/
boolean writeLDIF)
throws DirectoryException
{
try
{
try
{
}
catch (Exception e)
{
m, e);
}
try
{
while (true)
{
try
{
if (e == null)
{
break;
}
}
catch (LDIFException le)
{
if (! le.canContinueReading())
{
throw new DirectoryException(
}
else
{
continue;
}
}
// Make sure that we don't already have an entry with the same DN. If
// a duplicate is encountered, then log a message and continue.
{
continue;
}
// If the entry DN is a base DN, then add it with no more processing.
{
continue;
}
// Make sure that the parent exists. If not, then reject the entry.
boolean isBelowBaseDN = false;
{
{
isBelowBaseDN = true;
break;
}
}
if (! isBelowBaseDN)
{
continue;
}
{
continue;
}
// The entry does not exist but its parent does, so add it and update
// the set of children for the parent.
if (childDNSet == null)
{
}
}
if (writeLDIF)
{
writeLDIF();
}
}
catch (DirectoryException de)
{
throw de;
}
catch (Exception e)
{
m, e);
}
finally
{
}
}
finally
{
}
}
/** {@inheritDoc} */
throws DirectoryException
{
}
/** {@inheritDoc} */
throws DirectoryException
{
}
/** {@inheritDoc} */
throws DirectoryException
{
}
/** {@inheritDoc} */
{
{
{
}
}
}
/** {@inheritDoc} */
{
boolean configAcceptable = true;
// Make sure that there is only a single base DN.
{
configAcceptable = false;
}
return configAcceptable;
}
/** {@inheritDoc} */
{
// We don't actually need to do anything in response to this. However, if
// the base DNs or LDIF file are different from what we're currently using
// then indicate that admin action is required.
if (ldifFilePath != null)
{
{
ccr.setAdminActionRequired(true);
}
}
{
ccr.setAdminActionRequired(true);
}
return ccr;
}
/** {@inheritDoc} */
public DN getComponentEntryDN()
{
return currentConfig.dn();
}
/** {@inheritDoc} */
public String getClassName()
{
return LDIFBackend.class.getName();
}
/** {@inheritDoc} */
{
return alerts;
}
/** {@inheritDoc} */
public void preloadEntryCache() throws UnsupportedOperationException {
throw new UnsupportedOperationException("Operation not supported.");
}
}