ConfigFileHandler.java revision 83efeeb66bc183b219c55fb6b96f9f78e997e0ad
/*
* 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
* 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
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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
*
*
* Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
/**
* This class defines a simple configuration handler for the Directory Server
* that will read the server configuration from an LDIF file.
*/
public class ConfigFileHandler
extends ConfigHandler
implements AlertGenerator
{
/**
* The tracer object for the debug logger.
*/
/**
* The fully-qualified name of this class.
*/
private static final String CLASS_NAME =
"org.opends.server.extensions.ConfigFileHandler";
/**
* The set of supported control OIDs for this backend.
*/
/**
* The set of supported feature OIDs for this backend.
*/
/**
* The privilege array containing both the CONFIG_READ and CONFIG_WRITE
* privileges.
*/
private static final Privilege[] CONFIG_READ_AND_WRITE =
{
};
// Indicates whether to maintain a configuration archive.
private boolean maintainConfigArchive;
// Indicates whether to start using the last known good configuration.
private boolean useLastKnownGoodConfig;
// A SHA-1 digest of the last known configuration. This should only be
// incorrect if the server configuration file has been manually edited with
// the server online, which is a bad thing.
private byte[] configurationDigest;
// The mapping that holds all of the configuration entries that have been read
// from the LDIF file.
// The reference to the configuration root entry.
private ConfigEntry configRootEntry;
// The set of base DNs for this config handler backend.
// The maximum config archive size to maintain.
private int maxConfigArchiveSize;
// The write lock used to ensure that only one thread can apply a
// configuration update at any given time.
private Object configLock;
// The path to the configuration file.
private String configFile;
// The instance root directory for the Directory Server.
private String serverRoot;
/**
* Creates a new instance of this config file handler. No initialization
* should be performed here, as all of that work should be done in the
* <CODE>initializeConfigHandler</CODE> method.
*/
public ConfigFileHandler()
{
super();
}
/**
* {@inheritDoc}
*/
@Override()
throws InitializationException
{
// Initialize the config lock.
configLock = new Object();
// Determine whether we should try to start using the last known good
// configuration. If so, then only do so if such a file exists. If it
// doesn't exist, then fall back on the active configuration file.
this.configFile = configFile;
{
if (! f.exists())
{
configFile));
useLastKnownGoodConfig = false;
f = new File(configFile);
}
else
{
configFile));
}
}
else
{
f = new File(configFile);
}
try
{
if (! f.exists())
{
f.getAbsolutePath());
throw new InitializationException(message);
}
}
catch (InitializationException ie)
{
if (debugEnabled())
{
}
throw ie;
}
catch (Exception e)
{
if (debugEnabled())
{
}
throw new InitializationException(message);
}
// Check to see if a configuration archive exists. If not, then create one.
// If so, then check whether the current configuration matches the last
// configuration in the archive. If it doesn't, then archive it.
if (maintainConfigArchive & (! useLastKnownGoodConfig))
{
try
{
}
catch (DirectoryException de)
{
}
if (archiveDirectory.exists())
{
try
{
{
}
} catch (Exception e) {}
}
else
{
}
}
// Fixme -- Should we add a hash or signature check here?
// See if there is a config changes file. If there is, then try to apply
// the changes contained in it.
try
{
if (changesFile.exists())
{
{
}
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
throw new InitializationException(message, e);
}
// We will use the LDIF reader to read the configuration file. Create an
// LDIF import configuration to do this and then get the reader.
try
{
// FIXME -- Should we support encryption or compression for the config?
}
catch (Exception e)
{
if (debugEnabled())
{
}
throw new InitializationException(message, e);
}
// Read the first entry from the configuration file.
try
{
}
catch (LDIFException le)
{
if (debugEnabled())
{
}
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
try
{
}
{
if (debugEnabled())
{
}
}
throw new InitializationException(message, e);
}
// Make sure that the provide LDIF file is not empty.
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
throw new InitializationException(message);
}
// Make sure that the DN of this entry is equal to the config root DN.
try
{
{
throw new InitializationException(message);
}
}
catch (InitializationException ie)
{
if (debugEnabled())
{
}
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
throw ie;
}
catch (Exception e)
{
if (debugEnabled())
{
}
try
{
}
{
if (debugEnabled())
{
}
}
// This should not happen, so we can use a generic error here.
throw new InitializationException(message, e);
}
// Convert the entry to a configuration entry and put it in the config
// hash.
// Iterate through the rest of the configuration file and process the
// remaining entries.
while (true)
{
// Read the next entry from the configuration.
try
{
}
catch (LDIFException le)
{
if (debugEnabled())
{
}
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
try
{
}
{
if (debugEnabled())
{
}
}
throw new InitializationException(message, e);
}
// If the entry is null, then we have reached the end of the configuration
// file.
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
break;
}
// Make sure that the DN of the entry read doesn't already exist.
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
f.getAbsolutePath());
throw new InitializationException(message);
}
// Make sure that the parent DN of the entry read does exist.
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
f.getAbsolutePath());
throw new InitializationException(message);
}
if (parentEntry == null)
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
throw new InitializationException(message);
}
// Create the new configuration entry, add it as a child of the provided
// parent entry, and put it into the entry has.
try
{
}
catch (Exception e)
{
// This should not happen.
if (debugEnabled())
{
}
try
{
}
{
if (debugEnabled())
{
}
}
throw new InitializationException(message, e);
}
}
// Determine the appropriate server root. If it's not defined in the
// environment config, then try to figure it out from the location of the
// configuration file.
{
try
{
if ((configDirFile != null) &&
{
}
if (serverRoot == null)
{
throw new InitializationException(message);
}
}
catch (InitializationException ie)
{
if (debugEnabled())
{
}
throw ie;
}
catch (Exception e)
{
if (debugEnabled())
{
}
throw new InitializationException(message);
}
}
else
{
}
// Register with the Directory Server as an alert generator.
// Register with the Directory Server as the backend that should be used
// when accessing the configuration.
try
{
// Set a backend ID for the config backend. Try to avoid potential
// conflict with user backend identifiers.
setBackendID("__config.ldif__");
}
catch (Exception e)
{
if (debugEnabled())
{
}
throw new InitializationException(message, e);
}
}
/**
* Calculates a SHA-1 digest of the current configuration file.
*
* @return The calculated configuration digest.
*
* @throws DirectoryException If a problem occurs while calculating the
* digest.
*/
private byte[] calculateConfigDigest()
throws DirectoryException
{
try
{
byte[] buffer = new byte[8192];
while (true)
{
if (bytesRead < 0)
{
break;
}
}
return sha1Digest.digest();
}
catch (Exception e)
{
message, e);
}
finally
{
if (inputStream != null)
{
try
{
inputStream.close();
}
catch (IOException e) {
// ignore;
}
}
}
}
/**
* Looks at the existing archive directory, finds the latest archive file,
* and calculates a SHA-1 digest of that file.
*
* @return The calculated digest of the most recent archived configuration
* file.
*
* @throws DirectoryException If a problem occurs while calculating the
* digest.
*/
throws DirectoryException
{
int latestCounter = 0;
long latestTimestamp = -1;
{
{
continue;
}
if (dotPos < 0)
{
continue;
}
if (dashPos < 0)
{
try
{
if (timestamp > latestTimestamp)
{
latestCounter = 0;
continue;
}
}
catch (Exception e)
{
continue;
}
}
else
{
try
{
if (timestamp > latestTimestamp)
{
continue;
}
{
continue;
}
}
catch (Exception e)
{
continue;
}
}
}
if (latestFileName == null)
{
return null;
}
try
{
byte[] buffer = new byte[8192];
while (true)
{
if (bytesRead < 0)
{
break;
}
}
return sha1Digest.digest();
}
catch (Exception e)
{
message, e);
}
}
/**
* Applies the updates in the provided changes file to the content in the
* specified source file. The result will be written to a temporary file, the
* current source file will be moved out of place, and then the updated file
* will be moved into the place of the original file. The changes file will
* also be renamed so it won't be applied again.
* <BR><BR>
* If any problems are encountered, then the config initialization process
* will be aborted.
*
* @param sourceFile The LDIF file containing the source data.
* @param changesFile The LDIF file containing the changes to apply.
*
* @throws IOException If a problem occurs while performing disk I/O.
*
* @throws LDIFException If a problem occurs while trying to interpret the
* data.
*/
throws IOException, LDIFException
{
// Create the appropriate LDIF readers and writer.
importConfig.setValidateSchema(false);
importConfig.setValidateSchema(false);
// Apply the changes and make sure there were no errors.
try
{
} catch (Exception e) {}
try
{
} catch (Exception e) {}
try
{
} catch (Exception e) {}
if (! successful)
{
// FIXME -- Log each error message and throw an exception.
{
}
throw new LDIFException(message);
}
// Move the current config file out of the way and replace it with the
// updated version.
{
}
// Move the changes file out of the way so it doesn't get applied again.
if (newChanges.exists())
{
newChanges.delete();
}
}
/**
* {@inheritDoc}
*/
@Override()
public void finalizeConfigHandler()
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
}
/**
* {@inheritDoc}
*/
@Override()
public void finalizeBackend()
{
// No implementation is required.
}
/**
* {@inheritDoc}
*/
@Override()
public ConfigEntry getConfigRootEntry()
throws ConfigException
{
return configRootEntry;
}
/**
* {@inheritDoc}
*/
@Override()
throws ConfigException
{
}
/**
* {@inheritDoc}
*/
@Override()
public String getServerRoot()
{
return serverRoot;
}
/**
* {@inheritDoc}
*/
@Override()
throws ConfigException
{
// No action is required.
}
/**
* {@inheritDoc}
*/
@Override()
public void initializeBackend()
{
// No action is required, since all initialization was performed in the
// initializeConfigHandler method.
}
/**
* {@inheritDoc}
*/
@Override()
public DN[] getBaseDNs()
{
return baseDNs;
}
/**
* {@inheritDoc}
*/
@Override()
public long getEntryCount()
{
return configEntries.size();
}
/**
* {@inheritDoc}
*/
@Override()
public boolean isLocal()
{
// The configuration information will always be local.
return true;
}
/**
* {@inheritDoc}
*/
@Override()
{
// All searches in this backend will always be considered indexed.
return true;
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
if(ret < 0)
{
return ConditionResult.UNDEFINED;
}
else if(ret == 0)
{
return ConditionResult.FALSE;
}
else
{
return ConditionResult.TRUE;
}
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
{
return -1;
}
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
if (configEntry == null)
{
return null;
}
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// If there is an add operation, then make sure that the associated user has
// both the CONFIG_READ and CONFIG_WRITE privileges.
if (addOperation != null)
{
addOperation)))
{
message);
}
}
// Grab the config lock to ensure that only one config update may be in
// progress at any given time.
synchronized (configLock)
{
// Make sure that the target DN does not already exist. If it does, then
// fail.
{
}
// Make sure that the entry's parent exists. If it does not, then fail.
{
// The entry DN doesn't have a parent. This is not allowed.
}
if (parentEntry == null)
{
// The parent entry does not exist. This is not allowed.
// Get the matched DN, if possible.
{
{
break;
}
}
}
// Encapsulate the provided entry in a config entry.
// See if the parent entry has any add listeners. If so, then iterate
// through them and make sure the new entry is acceptable.
for (ConfigAddListener l : addListeners)
{
{
throw new DirectoryException(
}
}
// At this point, we will assume that everything is OK and proceed with
// the add.
try
{
}
catch (ConfigException ce)
{
if (debugEnabled())
{
}
message);
}
// Notify all the add listeners that the entry has been added.
for (ConfigAddListener l : addListeners)
{
{
{
}
}
"applyConfigurationAdd");
}
{
{
{
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// If there is a delete operation, then make sure that the associated user
// has both the CONFIG_READ and CONFIG_WRITE privileges.
if (deleteOperation != null)
{
{
message);
}
}
// Grab the config lock to ensure that only one config update may be in
// progress at any given time.
synchronized (configLock)
{
// Get the target entry. If it does not exist, then fail.
{
// Try to find the matched DN if possible.
{
{
{
break;
}
}
}
}
// If the entry has children, then fail.
if (entry.hasChildren())
{
message);
}
// Get the parent entry. If there isn't one, then it must be the config
// root, which we won't allow.
if (parentEntry == null)
{
}
// Get the delete listeners from the parent and make sure that they are
// all OK with the delete.
for (ConfigDeleteListener l : deleteListeners)
{
{
message);
}
}
// At this point, we will assume that everything is OK and proceed with
// the delete.
try
{
}
catch (ConfigException ce)
{
if (debugEnabled())
{
}
message);
}
// Notify all the delete listeners that the entry has been removed.
for (ConfigDeleteListener l : deleteListeners)
{
{
{
}
}
"applyConfigurationDelete");
}
{
{
{
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// If there is a modify operation, then make sure that the associated user
// has both the CONFIG_READ and CONFIG_WRITE privileges. Also, if the
// operation targets the set of root privileges then make sure the user has
// the PRIVILEGE_CHANGE privilege.
if (modifyOperation != null)
{
{
message);
}
true);
{
{
{
message);
}
break;
}
}
}
// Grab the config lock to ensure that only one config update may be in
// progress at any given time.
synchronized (configLock)
{
// Get the DN of the target entry for future reference.
// Get the target entry. If it does not exist, then fail.
if (currentEntry == null)
{
// Try to find the matched DN if possible.
{
{
{
break;
}
}
}
}
// If the structural class is different between the current entry and the
// new entry, then reject the change.
{
}
// Create a new config entry to use for the validation testing.
// See if there are any config change listeners registered for this entry.
// If there are, then make sure they are all OK with the change.
for (ConfigChangeListener l : changeListeners)
{
{
throw new DirectoryException(
}
}
// At this point, it looks like the change is acceptable, so apply it.
// We'll just overwrite the core entry in the current config entry so that
// we keep all the registered listeners, references to the parent and
// children, and other metadata.
currentEntry.setEntry(e);
// Notify all the change listeners of the update.
for (ConfigChangeListener l : changeListeners)
{
{
{
}
}
"applyConfigurationChange");
}
{
{
{
}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// If there is a modify DN operation, then make sure that the associated
// user has both the CONFIG_READ and CONFIG_WRITE privileges.
if (modifyDNOperation != null)
{
{
message);
}
}
// Modify DN operations will not be allowed in the configuration, so this
// will always throw an exception.
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// Make sure that the associated user has the CONFIG_READ privilege.
{
message);
}
// First, get the base DN for the search and make sure that it exists.
{
{
{
{
break;
}
}
}
}
// Get the scope for the search and perform the remainder of the processing
// accordingly. Also get the filter since we will need it in all cases.
switch (scope)
{
case BASE_OBJECT:
// We are only interested in the base entry itself. See if it matches
// and if so then return the entry.
if (filter.matchesEntry(e))
{
}
break;
case SINGLE_LEVEL:
// We are only interested in entries immediately below the base entry.
// Iterate through them and return the ones that match the filter.
{
if (filter.matchesEntry(e))
{
{
break;
}
}
}
break;
case WHOLE_SUBTREE:
// We are interested in the base entry and all its children. Use a
// recursive process to achieve this.
break;
case SUBORDINATE_SUBTREE:
// We are not interested in the base entry, but we want to check out all
// of its children. Use a recursive process to achieve this.
{
{
break;
}
}
break;
default:
// The user provided an invalid scope.
}
}
/**
* Performs a subtree search starting at the provided base entry, returning
* all entries anywhere in that subtree that match the provided filter.
*
* @param baseEntry The base entry below which to perform the search.
* @param filter The filter to use to identify matching entries.
* @param searchOperation The search operation to use to return entries to
* the client.
*
* @return <CODE>true</CODE> if the search should continue, or
* <CODE>false</CODE> if it should stop for some reason (e.g., the
* time limit or size limit has been reached).
*
* @throws DirectoryException If a problem occurs during processing.
*/
throws DirectoryException
{
if (filter.matchesEntry(e))
{
{
return false;
}
}
{
{
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override()
public void writeUpdatedConfig()
throws DirectoryException
{
// FIXME -- This needs support for encryption.
// Calculate an archive for the current server configuration file and see if
// it matches what we expect. If not, then the file has been manually
// edited with the server online which is a bad thing. In that case, we'll
// copy the current config off to the side before writing the new config
// so that the manual changes don't get lost but also don't get applied.
// Also, send an admin alert notifying administrators about the problem.
{
try
{
byte[] currentDigest = calculateConfigDigest();
{
"config.manualedit-" +
int counter = 2;
while (newConfigFile.exists())
{
counter++);
}
byte[] buffer = new byte[8192];
while (true)
{
if (bytesRead < 0)
{
break;
}
}
inputStream.close();
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
}
// Write the new configuration to a temporary file.
try
{
// FIXME -- Add all the appropriate configuration options.
}
catch (Exception e)
{
if (debugEnabled())
{
}
return;
}
// Delete the previous version of the configuration and rename the new one.
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
return;
}
// Try to write the archive for the new configuration.
{
}
}
/**
* Writes the current configuration to the configuration archive. This will
* be a best-effort attempt.
*/
private void writeConfigArchive()
{
if (! maintainConfigArchive)
{
return;
}
// Determine the path to the directory that will hold the archived
// configuration files.
// If the archive directory doesn't exist, then create it.
if (! archiveDirectory.exists())
{
try
{
if (! archiveDirectory.mkdirs())
{
return;
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
return;
}
}
// Determine the appropriate name to use for the current configuration.
try
{
if (archiveFile.exists())
{
int counter = 2;
while (archiveFile.exists())
{
counter++;
}
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
return;
}
// Copy the current configuration to the new configuration file.
byte[] buffer = new byte[8192];
try
{
while (bytesRead > 0)
{
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
return;
}
finally
{
try
{
inputStream.close();
} catch (Exception e) {}
try
{
} catch (Exception e) {}
}
// If we should enforce a maximum number of archived configurations, then
// see if there are any old ones that we need to delete.
if (maxConfigArchiveSize > 0)
{
if (numToDelete > 0)
{
{
{
continue;
}
// Simply ordering by filename should work, even when there are
// timestamp conflicts, because the dash comes before the period in
// the ASCII character set.
}
{
try
{
f.delete();
} catch (Exception e) {}
}
}
}
}
/**
* {@inheritDoc}
*/
@Override()
public void writeSuccessfulStartupConfig()
{
{
// The server was started with the "last known good" configuration, so we
// shouldn't overwrite it with something that is probably bad.
return;
}
// Copy the current config file to a temporary file.
try
{
try
{
try
{
byte[] buffer = new byte[8192];
while (true)
{
if (bytesRead < 0)
{
break;
}
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
getExceptionMessage(e)));
return;
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
getExceptionMessage(e)));
return;
}
finally
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
getExceptionMessage(e)));
return;
}
finally
{
try
{
inputStream.close();
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
}
// If a ".startok" file already exists, then move it to an ".old" file.
try
{
{
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
try
{
if (startOKFile.exists())
{
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
// Rename the temp file to the ".startok" file.
try
{
} catch (Exception e)
{
if (debugEnabled())
{
}
getExceptionMessage(e)));
return;
}
// Remove the ".old" file if there is one.
try
{
{
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
}
}
/**
* {@inheritDoc}
*/
@Override()
{
return SUPPORTED_CONTROLS;
}
/**
* {@inheritDoc}
*/
@Override()
{
return SUPPORTED_FEATURES;
}
/**
* {@inheritDoc}
*/
@Override()
public boolean supportsLDIFExport()
{
// TODO We would need export-ldif to initialize this backend.
return false;
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// TODO We would need export-ldif to initialize this backend.
}
/**
* Writes the current configuration to LDIF with the provided export
* configuration.
*
* @param exportConfig The configuration to use for the export.
*
* @throws DirectoryException If a problem occurs while writing the LDIF.
*/
throws DirectoryException
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
message, e);
}
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
message, e);
}
}
/**
* Writes the provided entry and any children that it may have to the provided
* LDIF writer.
*
* @param writer The LDIF writer to use to write the entry and its
* children.
* @param configEntry The configuration entry to write, along with its
* children.
*
* @throws DirectoryException If a problem occurs while attempting to write
* the entry or one of its children.
*/
throws DirectoryException
{
try
{
// Write the entry itself to LDIF.
}
catch (Exception e)
{
if (debugEnabled())
{
}
message, e);
}
// See if the entry has any children. If so, then iterate through them and
// write them and their children. We'll copy the entries into a tree map
// so that we have a sensible order in the resulting LDIF.
{
}
}
/**
* {@inheritDoc}
*/
@Override()
public boolean supportsLDIFImport()
{
return false;
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
}
/**
* {@inheritDoc}
*/
@Override()
public boolean supportsBackup()
{
// We do support an online backup mechanism for the configuration.
return true;
}
/**
* {@inheritDoc}
*/
@Override()
{
// We should support online backup for the configuration in any form. This
// implementation does not support incremental backups, but in this case
// even if we're asked to do an incremental we'll just do a full backup
// instead. So the answer to this should always be "true".
return true;
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// Get the properties to use for the backup. We don't care whether or not
// it's incremental, so there's no need to get that.
// Create a hash map that will hold the extra backup property information
// for this backup.
// Get the crypto manager and use it to obtain references to the message
if (hash)
{
if (signHash)
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
throw new DirectoryException(
e);
}
}
else
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
throw new DirectoryException(
e);
}
}
}
// Create an output stream that will be used to write the archive file. At
// its core, it will be a file output stream to put a file on the disk. If
// we are to encrypt the data, then that file output stream will be wrapped
// in a cipher output stream. The resulting output stream will then be
// wrapped by a zip output stream (which may or may not actually use
// compression).
try
{
filename);
if (archiveFile.exists())
{
int i=1;
while (true)
{
filename + "." + i);
if (archiveFile.exists())
{
i++;
}
else
{
break;
}
}
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
message, e);
}
// If we should encrypt the data, then wrap the output stream in a cipher
// output stream.
if (encrypt)
{
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
message, e);
}
}
// Wrap the file output stream in a zip output stream.
backupID);
if (compress)
{
}
else
{
}
// This may seem a little weird, but in this context, we only have access to
// this class as a backend and not as the config handler. We need it as a
// config handler to determine the path to the config file, so we can get
// that from the Directory Server object.
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
get(getExceptionMessage(e));
message, e);
}
// Read the Directory Server configuration file and put it in the archive.
byte[] buffer = new byte[8192];
try
{
inputStream = new FileInputStream(f);
while (true)
{
if (bytesRead < 0)
{
break;
}
if (hash)
{
if (signHash)
{
}
else
{
}
}
}
inputStream.close();
}
catch (Exception e)
{
if (debugEnabled())
{
}
try
{
inputStream.close();
try
{
message, e);
}
// If an archive directory exists, then add its contents as well.
try
{
if (archiveDirectory.exists())
{
{
archiveFile.getName());
while (true)
{
if (bytesRead < 0)
{
break;
}
if (hash)
{
if (signHash)
{
}
else
{
}
}
}
inputStream.close();
}
}
}
catch (Exception e)
{
if (debugEnabled())
{
}
try
{
inputStream.close();
try
{
message, e);
}
// We're done writing the file, so close the zip stream (which should also
// close the underlying stream).
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
message, e);
}
// Get the digest or MAC bytes if appropriate.
byte[] digestBytes = null;
if (hash)
{
if (signHash)
{
}
else
{
}
}
// Create the backup info structure for this backup and add it to the backup
// directory.
// FIXME -- Should I use the date from when I started or finished?
try
{
}
catch (Exception e)
{
if (debugEnabled())
{
}
message, e);
}
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// NYI
}
/**
* {@inheritDoc}
*/
@Override()
public boolean supportsRestore()
{
// We will provide a restore, but only for offline operations.
return true;
}
/**
* {@inheritDoc}
*/
@Override()
throws DirectoryException
{
// First, make sure that the requested backup exists.
if (backupInfo == null)
{
message);
}
// Read the backup info structure to determine the name of the file that
// contains the archive. Then make sure that file exists.
if (backupFilename == null)
{
message);
}
try
{
if (! backupFile.exists())
{
message);
}
}
catch (DirectoryException de)
{
throw de;
}
catch (Exception e)
{
message, e);
}
// If the backup is hashed, then we need to get the message digest to use
// to verify it.
if (unsignedHash != null)
{
if (digestAlgorithm == null)
{
message);
}
try
{
}
catch (Exception e)
{
message, e);
}
}
// If the backup is signed, then we need to get the MAC to use to verify it.
if (signedHash != null)
{
{
message);
}
try
{
}
catch (Exception e)
{
message, e);
}
}
// Create the input stream that will be used to read the backup file. At
// its core, it will be a file input stream.
try
{
}
catch (Exception e)
{
message, e);
}
// If the backup is encrypted, then we need to wrap the file input stream
// in a cipher input stream.
if (backupInfo.isEncrypted())
{
try
{
}
catch (Exception e)
{
message, e);
}
}
// Now wrap the resulting input stream in a zip stream so that we can read
// its contents. We don't need to worry about whether to use compression or
// not because it will be handled automatically.
// Determine whether we should actually do the restore, or if we should just
// try to verify the archive. If we are going to actually do the restore,
// then create a directory and move the existing config files there so that
// they can be restored in case something goes wrong.
if (! verifyOnly)
{
// Create a new directory to hold the current config files.
try
{
{
if (configBackupDir.exists())
{
int i=2;
while (true)
{
backupDirPath = configBackupDirPath + i;
if (configBackupDir.exists())
{
i++;
}
else
{
break;
}
}
}
if (archiveDirectory.exists())
{
}
}
}
catch (Exception e)
{
getExceptionMessage(e));
message, e);
}
// Create a new directory to hold the restored config files.
try
{
}
catch (Exception e)
{
// Try to restore the previous config directory if possible. This will
// probably fail in this case, but try anyway.
if (configBackupDir != null)
{
try
{
}
{
}
}
message, e);
}
}
// Read through the archive file an entry at a time. For each entry, update
// the digest or MAC if necessary, and if we're actually doing the restore,
// then write the files out into the config directory.
byte[] buffer = new byte[8192];
while (true)
{
try
{
}
catch (Exception e)
{
// Tell the user where the previous config was archived.
if (configBackupDir != null)
{
}
message, e);
}
{
break;
}
// Get the filename for the zip entry and update the digest or MAC as
// necessary.
{
}
{
}
// If we're doing the restore, then create the output stream to write the
// file.
if (! verifyOnly)
{
try
{
{
}
}
catch (Exception e)
{
// Tell the user where the previous config was archived.
if (configBackupDir != null)
{
}
throw new DirectoryException(
e);
}
}
// Read the contents of the file and update the digest or MAC as
// necessary. If we're actually restoring it, then write it into the
// new config directory.
try
{
while (true)
{
if (bytesRead < 0)
{
// We've reached the end of the entry.
break;
}
// Update the digest or MAC if appropriate.
{
}
{
}
// Write the data to the output stream if appropriate.
if (outputStream != null)
{
}
}
// We're at the end of the file so close the output stream if we're
// writing it.
if (outputStream != null)
{
}
}
catch (Exception e)
{
// Tell the user where the previous config was archived.
if (configBackupDir != null)
{
}
message, e);
}
}
// Close the zip stream since we don't need it anymore.
try
{
}
catch (Exception e)
{
message, e);
}
// At this point, we should be done with the contents of the ZIP file and
// the restore should be complete. If we were generating a digest or MAC,
// then make sure it checks out.
{
{
}
else
{
// Tell the user where the previous config was archived.
if (configBackupDir != null)
{
}
message);
}
}
{
{
}
else
{
// Tell the user where the previous config was archived.
if (configBackupDir != null)
{
}
message);
}
}
// If we are just verifying the archive, then we're done.
if (verifyOnly)
{
return;
}
// If we've gotten here, then the archive was restored successfully. Get
// rid of the temporary copy we made of the previous config directory and
// exit.
if (configBackupDir != null)
{
}
}
/**
* {@inheritDoc}
*/
public DN getComponentEntryDN()
{
return configRootEntry.getDN();
}
/**
* {@inheritDoc}
*/
public String getClassName()
{
return CLASS_NAME;
}
/**
* {@inheritDoc}
*/
{
return alerts;
}
/**
* Examines the provided result and logs a message if appropriate. If the
* result code is anything other than {@code SUCCESS}, then it will log an
* error message. If the operation was successful but admin action is
* required, then it will log a warning message. If no action is required but
* messages were generated, then it will log an informational message.
*
* @param result The config change result object that
* @param entryDN The DN of the entry that was added, deleted, or
* modified.
* @param className The name of the class for the object that generated the
* provided result.
* @param methodName The name of the method that generated the provided
* result.
*/
{
{
return;
}
{
{
{
}
messageBuffer.append(s);
}
}
{
}
else if (adminActionRequired)
{
}
{
}
}
}