package io.eguan.vvr.configuration; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import io.eguan.configuration.AbstractConfigKey; import io.eguan.configuration.AbstractConfigurationContext; import io.eguan.configuration.ConfigValidationException; import io.eguan.configuration.MetaConfiguration; import io.eguan.configuration.ValidationError; import io.eguan.configuration.ValidationError.ErrorType; import io.eguan.vvr.configuration.keys.IbsAutoConfRamSize; import io.eguan.vvr.configuration.keys.IbsBufferRotationDelay; import io.eguan.vvr.configuration.keys.IbsBufferRotationThreshold; import io.eguan.vvr.configuration.keys.IbsBufferWriteDelayIncrement; import io.eguan.vvr.configuration.keys.IbsBufferWriteDelayLevelSize; import io.eguan.vvr.configuration.keys.IbsBufferWriteDelayThreshold; import io.eguan.vvr.configuration.keys.IbsCompressionConfigKey; import io.eguan.vvr.configuration.keys.IbsConfigKey; import io.eguan.vvr.configuration.keys.IbsDisableBackgroundCompactionForIbpgenConfigKey; import io.eguan.vvr.configuration.keys.IbsDumpAtStopBestEffortDelayConfigKey; import io.eguan.vvr.configuration.keys.IbsHotDataConfigKey; import io.eguan.vvr.configuration.keys.IbsIbpGenPathConfigKey; import io.eguan.vvr.configuration.keys.IbsIbpPathConfigKey; import io.eguan.vvr.configuration.keys.IbsLdbBlockRestartIntervalConfigKey; import io.eguan.vvr.configuration.keys.IbsLdbBlockSizeConfigKey; import io.eguan.vvr.configuration.keys.IbsLogLevelConfigKey; import io.eguan.vvr.configuration.keys.IbsOwnerUuidConfigKey; import io.eguan.vvr.configuration.keys.IbsRecordExecutionConfigKey; import io.eguan.vvr.configuration.keys.IbsSyslogConfigKey; import io.eguan.vvr.configuration.keys.IbsUuidConfigKey; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Objects; import java.util.Properties; import javax.annotation.Nonnull; /** * Context for configuration keys specific to the IBS subsystem. * * @author oodrive * @author pwehrle * @author ebredzinski * @author jmcaba * @author llambert * */ public final class IbsConfigurationContext extends AbstractConfigurationContext { private static final String NAME = "io.eguan.vvr.ibs"; private static final IbsConfigurationContext INSTANCE = new IbsConfigurationContext(); private static class ChildDirectoryCollector extends SimpleFileVisitor<Path> { private Collection<File> childCollection; @Override public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException { final FileVisitResult result = super.preVisitDirectory(dir, attrs); this.childCollection.add(dir.toFile()); return result; } } public static IbsConfigurationContext getInstance() { return INSTANCE; } private IbsConfigurationContext() throws IllegalArgumentException, NullPointerException { super(NAME, new AbstractConfigKey[] { IbsIbpPathConfigKey.getInstance(), IbsIbpGenPathConfigKey.getInstance(), IbsHotDataConfigKey.getInstance(), IbsCompressionConfigKey.getInstance(), IbsUuidConfigKey.getInstance(), IbsOwnerUuidConfigKey.getInstance(), IbsLogLevelConfigKey.getInstance(), IbsLdbBlockSizeConfigKey.getInstance(), IbsLdbBlockRestartIntervalConfigKey.getInstance(), IbsBufferRotationThreshold.getInstance(), IbsBufferRotationDelay.getInstance(), IbsBufferWriteDelayThreshold.getInstance(), IbsBufferWriteDelayLevelSize.getInstance(), IbsBufferWriteDelayIncrement.getInstance(), IbsRecordExecutionConfigKey.getInstance(), IbsDumpAtStopBestEffortDelayConfigKey.getInstance(), IbsDisableBackgroundCompactionForIbpgenConfigKey.getInstance(), IbsAutoConfRamSize.getInstance(), IbsSyslogConfigKey.getInstance() }); } @Override public final List<ValidationError> validateConfiguration(final MetaConfiguration configuration) { final List<ValidationError> result = super.validateConfiguration(configuration); final ArrayList<File> ibpPaths = IbsIbpPathConfigKey.getInstance().getTypedValue(configuration); final File ibpGenPath = IbsIbpGenPathConfigKey.getInstance().getTypedValue(configuration); if (ibpPaths == null) { return result; } result.addAll(checkForPathCollisions(ibpPaths)); if (ibpGenPath == null) { return result; } result.addAll(checkForPathCollisions(ibpGenPath, ibpPaths)); return result; } /** * Stores the values belonging to the native IBS configuration to the given {@link OutputStream}. * * @param configuration * the {@link MetaConfiguration} from which to read the values * @param outputStream * the {@link OutputStream} to which to write * @throws IOException * if storing to the {@link OutputStream} fails * @throws IllegalStateException * if the {@link MetaConfiguration} was not created with {@link IbsConfigurationContext} * @throws NullPointerException * if either argument is <code>null</code> */ public final void storeIbsConfig(@Nonnull final MetaConfiguration configuration, @Nonnull final OutputStream outputStream) throws IOException, IllegalStateException, NullPointerException { Objects.requireNonNull(configuration); Objects.requireNonNull(outputStream); final Properties outputProps = new Properties(); for (final AbstractConfigKey currKey : getConfigKeys()) { // TODO: add a key that's not an IbsConfigKey to this context or // remove this if (!(currKey instanceof IbsConfigKey)) { continue; } final IbsConfigKey currIbsKey = (IbsConfigKey) currKey; outputProps.setProperty(currIbsKey.getBackendConfigKey(), currIbsKey.getBackendConfigValue(configuration)); } outputProps.store(outputStream, "Automatically generated IBS configuration file, do not modify"); } /** * Checks for path collisions (i.e. identical or subpaths of each other) for all elements of the given list. * * This method does not verify if the given {@link File} objects point to directories or files. * * @param dirList * a {@link List} of {@link File}s to check * @return a list of {@link ValidationError} to include in a {@link ConfigValidationException} */ private static List<ValidationError> checkForPathCollisions(final List<File> dirList) { final ArrayList<ValidationError> report = new ArrayList<ValidationError>(); final int size = dirList.size(); for (final ListIterator<File> iter = dirList.listIterator(); iter.hasNext();) { final File currFile = iter.next(); report.addAll(checkForPathCollisions(currFile, dirList.subList(iter.nextIndex(), size))); } return report; } /** * Checks if any of the given list's directories are identical or children or parents of the first directory. * * All files passed to this method must exist and be directories. * * @param file * the {@link File directory} to compare * @param fileList * the list of {@link File directories} to check for children or parents of the first file * @return a list of {@link ValidationError} to include in a {@link ConfigValidationException} */ private static List<ValidationError> checkForPathCollisions(final File file, final List<File> fileList) { final ArrayList<ValidationError> report = new ArrayList<ValidationError>(); final ChildDirectoryCollector collector = new ChildDirectoryCollector(); collector.childCollection = new HashSet<File>(); collector.childCollection.add(file); try { Files.walkFileTree(file.toPath(), collector); } catch (final IOException e) { report.add(new ValidationError(ErrorType.VALUE_INVALID, null, IbsIbpGenPathConfigKey.getInstance(), fileList, "exception while getting children of " + file + ": " + e.getMessage())); } if (!Collections.disjoint(collector.childCollection, fileList)) { report.add(new ValidationError(ErrorType.VALUE_INVALID, null, IbsIbpGenPathConfigKey.getInstance(), fileList, "common directories found between the children of " + file + ": " + collector.childCollection + " and the following directories:" + fileList)); } File parent = file.getParentFile(); final HashSet<File> parentSet = new HashSet<File>(); while (parent != null) { parentSet.add(parent); parent = parent.getParentFile(); } if (!Collections.disjoint(parentSet, fileList)) { report.add(new ValidationError(ErrorType.VALUE_INVALID, null, IbsIbpGenPathConfigKey.getInstance(), fileList, "Common directories found between the parents of " + file + ": " + parentSet + " and the following directories:" + fileList)); } return report; } }