All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gs.obevo.impl.AbstractEnvironmentEnricher Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2017 Goldman Sachs.
 * 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.
 */
package com.gs.obevo.impl;

import java.util.List;
import java.util.regex.Pattern;

import com.gs.obevo.api.appdata.Environment;
import com.gs.obevo.api.appdata.Schema;
import com.gs.obevo.api.factory.EnvironmentEnricher;
import com.gs.obevo.api.factory.PlatformConfiguration;
import com.gs.obevo.api.platform.ChangeType;
import com.gs.obevo.api.platform.Platform;
import com.gs.obevo.util.CollectionUtil;
import com.gs.obevo.util.VisibleForTesting;
import com.gs.obevo.util.vfs.FileObject;
import org.apache.commons.configuration2.CombinedConfiguration;
import org.apache.commons.configuration2.HierarchicalConfiguration;
import org.apache.commons.configuration2.ImmutableHierarchicalConfiguration;
import org.apache.commons.configuration2.tree.OverrideCombiner;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.vfs2.FileType;
import org.eclipse.collections.api.block.function.Function;
import org.eclipse.collections.api.block.predicate.Predicate;
import org.eclipse.collections.api.collection.ImmutableCollection;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.multimap.set.MutableSetMultimap;
import org.eclipse.collections.api.set.ImmutableSet;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.impl.block.factory.HashingStrategies;
import org.eclipse.collections.impl.factory.HashingStrategySets;
import org.eclipse.collections.impl.factory.Lists;
import org.eclipse.collections.impl.factory.Maps;
import org.eclipse.collections.impl.factory.Multimaps;
import org.eclipse.collections.impl.list.fixed.ArrayAdapter;
import org.eclipse.collections.impl.list.mutable.ListAdapter;

public abstract class AbstractEnvironmentEnricher implements EnvironmentEnricher {
    private static final Pattern PATTERN = Pattern.compile("[\\w+]+");
    private final PlatformConfiguration dbPlatformConfiguration = PlatformConfiguration.getInstance();

    @Override
    public ImmutableCollection readSystem(final HierarchicalConfiguration sysCfg, final FileObject sourcePath) {
        final Platform systemDbPlatform = dbPlatformConfiguration.valueOf(sysCfg.getString("type"));

        // Check for dbEnvironments first for backwards-compatibility
        ImmutableList envConfigs = iterConfigMutable(sysCfg, "environments.dbEnvironment");
        if (envConfigs.isEmpty()) {
            envConfigs = iterConfigMutable(sysCfg, "environments.environment");
        }

        ImmutableList envList = envConfigs.collect(new Function() {
            @Override
            public E valueOf(HierarchicalConfiguration envCfg) {
                E dbEnv = AbstractEnvironmentEnricher.this.createNewEnv();

                // combining the sys and env configurations before passing to downstream methods so that we can support only having env configs passed in
                CombinedConfiguration combinedConfiguration = new CombinedConfiguration(new OverrideCombiner());
                combinedConfiguration.addConfiguration(envCfg);
                combinedConfiguration.addConfiguration(sysCfg);
                combinedConfiguration.setExpressionEngine(sysCfg.getExpressionEngine());

                AbstractEnvironmentEnricher.this.enrich(dbEnv, combinedConfiguration, sourcePath, systemDbPlatform);
                AbstractEnvironmentEnricher.this.createEnv(dbEnv, combinedConfiguration, systemDbPlatform);
                return dbEnv;
            }
        });

        CollectionUtil.verifyNoDuplicates(envList, new Function() {
            @Override
            public Object valueOf(E e) {
                return e.getName();
            }
        }, "Invalid configuration from " + sourcePath + "; not expecting duplicate env names");
        return envList;
    }

    @Override
    public E readEnvironment(ImmutableHierarchicalConfiguration combinedConfiguration, FileObject sourcePath) {
        E dbEnv = createNewEnv();

        Platform systemDbPlatform = dbPlatformConfiguration.valueOf(combinedConfiguration.getString("type"));
        enrich(dbEnv, combinedConfiguration, sourcePath, systemDbPlatform);
        createEnv(dbEnv, combinedConfiguration, systemDbPlatform);

        return dbEnv;
    }

    protected abstract E createNewEnv();

    protected abstract void createEnv(E env, ImmutableHierarchicalConfiguration envCfg, Platform systemDbPlatform);

    private void enrich(Environment dbEnv, ImmutableHierarchicalConfiguration envCfg, FileObject sourcePath, Platform systemDbPlatform) {
        ImmutableList sourceDirs = iterString(envCfg, "sourceDirs");
        ImmutableSet acceptedExtensions = iterString(envCfg, "acceptedExtensions").toSet().toImmutable();
        FileObject rootDir = sourcePath.getType() == FileType.FILE ? sourcePath.getParent() : sourcePath;

        // Use coreSourcePath and additionalSourceDirs here (instead of setSourceDirs) to facilitate any external integrations
        dbEnv.setCoreSourcePath(rootDir);
        dbEnv.setAdditionalSourceDirs(sourceDirs);
        dbEnv.setAcceptedExtensions(acceptedExtensions);

        dbEnv.setCleanBuildAllowed(envCfg.getBoolean("cleanBuildAllowed", false));

        MutableMap tokens = iterConfig(envCfg, "tokens.token")
                .toMap(new Function() {
                    @Override
                    public String valueOf(ImmutableHierarchicalConfiguration tok) {
                        return tok.getString("key");
                    }
                }, new Function() {
                    @Override
                    public String valueOf(ImmutableHierarchicalConfiguration tok) {
                        return tok.getString("value");
                    }
                });
        dbEnv.setTokens(tokens.toImmutable());

        MutableMap runtimeEnvAttrs = iterConfig(envCfg, "runtimeEnvAttrs.runtimeEnvAttr")
                .toMap(new Function() {
                    @Override
                    public String valueOf(ImmutableHierarchicalConfiguration tok) {
                        return tok.getString("key");
                    }
                }, new Function() {
                    @Override
                    public String valueOf(ImmutableHierarchicalConfiguration tok) {
                        return tok.getString("value");
                    }
                });
        dbEnv.setRuntimeEnvAttrs(runtimeEnvAttrs.toImmutable());

        dbEnv.setRollbackDetectionEnabled(envCfg.getBoolean("rollbackDetectionEnabled", true));

        Integer metadataLineReaderVersion = envCfg.getInteger("metadataLineReaderVersion", null);
        if (metadataLineReaderVersion != null) {
            dbEnv.setMetadataLineReaderVersion(metadataLineReaderVersion);
        }

        dbEnv.setForceEnvInfraSetup(envCfg.getBoolean("forceEnvInfraSetup", null));

        String sourceEncoding = envCfg.getString("sourceEncoding");
        if (sourceEncoding != null) {
            dbEnv.setSourceEncoding(sourceEncoding);
        }
        Integer legacyDirectoryStructureEnabledVersion = envCfg.getInteger("legacyDirectoryStructureEnabled", null);
        if (legacyDirectoryStructureEnabledVersion != null) {
            dbEnv.setLegacyDirectoryStructureEnabledVersion(legacyDirectoryStructureEnabledVersion);
        }

        enrichSchemas(dbEnv, envCfg, systemDbPlatform);
    }

    private void enrichSchemas(Environment dbEnv, ImmutableHierarchicalConfiguration envCfg, final Platform systemDbPlatform) {
        dbEnv.setName(envCfg.getString("name"));
        dbEnv.setDefaultUserId(envCfg.getString("defaultUserId"));
        dbEnv.setDefaultPassword(envCfg.getString("defaultPassword"));

        final int schemaNameValidationVersion = envCfg.getInt("schemaNameValidation", dbPlatformConfiguration.getFeatureToggleVersion("schemaNameValidation"));

        // TODO add include/exclude schemas functionality
        ImmutableList schemaObjs = iterConfig(envCfg, "schemas.schema")
                .collect(new Function() {
                    @Override
                    public Schema valueOf(ImmutableHierarchicalConfiguration cfg) {
                        return AbstractEnvironmentEnricher.this.convertCfgToSchema(cfg, systemDbPlatform, schemaNameValidationVersion);
                    }
                });

        final MutableSet schemasToInclude = iterString(envCfg, "includeSchemas").toSet();
        final MutableSet schemasToExclude = iterString(envCfg, "excludeSchemas").toSet();

        if (!schemasToInclude.isEmpty() && !schemasToExclude.isEmpty()) {
            throw new IllegalArgumentException("Environment " + dbEnv.getName() + " has includeSchemas ["
                    + schemasToInclude + "] and excludeSchemas [" + schemasToExclude
                    + "] defined; please only specify one of them");
        } else if (!schemasToInclude.isEmpty()) {
            schemaObjs = schemaObjs.select(new Predicate() {
                @Override
                public boolean accept(Schema it) {
                    return schemasToInclude.contains(it.getName());
                }
            });
        } else if (!schemasToExclude.isEmpty()) {
            schemaObjs = schemaObjs.reject(new Predicate() {
                @Override
                public boolean accept(Schema it) {
                    return schemasToExclude.contains(it.getName());
                }
            });
        }

        MutableMap schemaNameOverrides = Maps.mutable.empty();
        ImmutableSet schemaNames = schemaObjs.collect(new Function() {
            @Override
            public String valueOf(Schema schema1) {
                return schema1.getName();
            }
        }).toSet().toImmutable();
        for (ImmutableHierarchicalConfiguration schemaOverride : iterConfig(envCfg, "schemaOverrides.schemaOverride")) {
            String schema = schemaOverride.getString("schema");
            if (schemaNames.contains(schema)) {
                schemaNameOverrides.put(schema, schemaOverride.getString("overrideValue"));
            } else {
                throw new IllegalArgumentException("Schema override definition value "
                        + schema + " is not defined in the schema list " + schemaNames + " for environment " + dbEnv.getName());
            }
        }

        dbEnv.setSchemaNameOverrides(schemaNameOverrides.toImmutable());
        // ensure that we only store the unique schema names here
        dbEnv.setSchemas(HashingStrategySets.mutable.ofAll(HashingStrategies.fromFunction(new Function() {
            @Override
            public String valueOf(Schema schema) {
                return schema.getName();
            }
        }), schemaObjs).toImmutable());
    }

    private Schema convertCfgToSchema(ImmutableHierarchicalConfiguration object, final Platform systemDbPlatform, final int schemaNameValidation) {
        String schemaName = object.getString("name");
        if (schemaNameValidation >= 2) {
            validateSchemaName(schemaName);
        }
        boolean readOnly = object.getBoolean("readOnly", false);

        MutableSetMultimap excludedNameMap = Multimaps.mutable.set.empty();

        ImmutableList excludes = iterConfig(object, "excludes");
        if (!excludes.isEmpty()) {
            if (excludes.size() > 1) {
                throw new IllegalArgumentException("Only expecting 1 excludes element under ");
            }
            ImmutableHierarchicalConfiguration excludesConfig = excludes.get(0);
            if (excludesConfig != null) {
                for (ChangeType changeType : systemDbPlatform.getChangeTypes()) {
                    ImmutableList excludedNames = iterListString(excludesConfig, changeType.getName().toLowerCase());
                    if (excludedNames.notEmpty()) {
                        excludedNameMap.putAll(changeType.getName(), excludedNames);
                    }

                    ImmutableList excludedPatterns = iterListString(excludesConfig, changeType.getName().toLowerCase() + "Pattern");
                    if (excludedPatterns.notEmpty()) {
                        throw new IllegalArgumentException("The Pattern element is deprecated. Use just the  element w/ wildcards (% or *)");
                    }
                }

                if (iterListString(excludesConfig, "procedure").notEmpty() || iterListString(excludesConfig, "procedurePattern").notEmpty()) {
                    throw new IllegalArgumentException("The procedure and procedurePattern elements are no longer supported. Use  only, with wildcards (% or *) if  needed");
                }
            }
        }

        return new Schema(schemaName, systemDbPlatform.getObjectExclusionPredicateBuilder().add(excludedNameMap.toImmutable()), readOnly);
    }

    @VisibleForTesting
    static void validateSchemaName(String schemaName) {
        if (!PATTERN.matcher(schemaName).matches()) {
            throw new IllegalArgumentException("SchemaName " + schemaName + " does not match regexp " + PATTERN.pattern());
        }
    }

    protected static ImmutableList iterConfig(ImmutableHierarchicalConfiguration c, String path) {
        List list = c.immutableConfigurationsAt(path);
        return list != null ? ListAdapter.adapt(list).toImmutable() : Lists.immutable.empty();
    }

    private static ImmutableList iterConfigMutable(HierarchicalConfiguration c, String path) {
        List list = c.configurationsAt(path);
        return list != null ? ListAdapter.adapt(list).toImmutable() : Lists.immutable.empty();
    }

    protected static ImmutableList iterString(ImmutableHierarchicalConfiguration c, String path) {
        String string = c.getString(path);
        if (!StringUtils.isBlank(string)) {
            String[] parts = string.trim().split("\\s*,\\s*");
            return ArrayAdapter.adapt(parts).toImmutable();
        }

        return Lists.immutable.empty();
    }

    private static ImmutableList iterListString(ImmutableHierarchicalConfiguration c, String path) {
        List list = c.getList(String.class, path);
        return list != null ? ListAdapter.adapt(list).toImmutable() : Lists.immutable.empty();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy