Package com.dtolabs.rundeck.server.storage

Source Code of com.dtolabs.rundeck.server.storage.StorageTreeFactory

package com.dtolabs.rundeck.server.storage;

import com.dtolabs.rundeck.core.common.Framework;
import com.dtolabs.rundeck.core.plugins.PluggableProviderService;
import com.dtolabs.rundeck.core.storage.*;
import com.dtolabs.rundeck.core.utils.PropertyUtil;
import com.dtolabs.rundeck.plugins.storage.StorageConverterPlugin;
import com.dtolabs.rundeck.plugins.storage.StoragePlugin;
import com.dtolabs.rundeck.server.plugins.ConfiguredPlugin;
import com.dtolabs.rundeck.server.plugins.PluginRegistry;
import com.dtolabs.rundeck.server.plugins.services.StorageConverterPluginProviderService;
import com.dtolabs.rundeck.server.plugins.services.StoragePluginProviderService;
import com.dtolabs.rundeck.server.plugins.storage.KeyStorageLayer;
import com.dtolabs.rundeck.server.plugins.storage.StorageLogger;
import org.apache.log4j.Logger;
import org.rundeck.storage.api.PathUtil;
import org.rundeck.storage.api.Tree;
import org.rundeck.storage.conf.TreeBuilder;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.InitializingBean;

import java.util.HashMap;
import java.util.Map;

/**
* StorageTreeFactory constructs Rundeck's StorageTree based on the configuration in the framework.properties
*
* @author greg
* @since 2/19/14 3:24 PM
*/
public class StorageTreeFactory implements FactoryBean<StorageTree>, InitializingBean {
    public static final String ORG_RUNDECK_STORAGE_EVENTS_LOGGER_NAME = "org.rundeck.storage.events";
    public static final String LOGGER_NAME = "loggerName";
    static Logger logger = Logger.getLogger(StorageTreeFactory.class);
    public static final String TYPE = "type";
    public static final String PATH = "path";
    public static final String CONFIG = "config";
    public static final String SEP = ".";
    public static final String REMOVE_PATH_PREFIX = "removePathPrefix";
    public static final String RESOURCE_SELECTOR = "resourceSelector";
    Framework rundeckFramework;
    private PluginRegistry pluginRegistry;
    private String storageConfigPrefix;
    private String converterConfigPrefix;
    private String baseStorageType;
    private String loggerName =ORG_RUNDECK_STORAGE_EVENTS_LOGGER_NAME;
    private Map<String, String> baseStorageConfig = new HashMap<String, String>();
    private Map<String, String> configuration = new HashMap<String, String>();

    private StoragePluginProviderService storagePluginProviderService;
    private StorageConverterPluginProviderService storageConverterPluginProviderService;

    //injected
    public void setRundeckFramework(Framework framework) {
        this.rundeckFramework = framework;
    }


    @Override
    public StorageTree getObject() throws Exception {
        if (null == rundeckFramework) {
            throw new FactoryBeanNotInitializedException("'rundeckFramework' is required");
        }
        if (null == pluginRegistry) {
            throw new FactoryBeanNotInitializedException("'pluginRegistry' is required");
        }
        if (null == storagePluginProviderService) {
            throw new FactoryBeanNotInitializedException("'storagePluginProviderService' is required");
        }
        if (null == storageConverterPluginProviderService) {
            throw new FactoryBeanNotInitializedException("'storageConverterPluginProviderService' is required");
        }
        if (null == storageConfigPrefix) {
            throw new FactoryBeanNotInitializedException("'storageConfigPrefix' is required");
        }
        if (null == converterConfigPrefix) {
            throw new FactoryBeanNotInitializedException("'converterConfigPrefix' is required");
        }
        if (null == baseStorageType) {
            throw new FactoryBeanNotInitializedException("'baseStorageType' is required");
        }
        return StorageUtil.asStorageTree(buildTree(configuration));
    }

    @Override
    public Class<?> getObjectType() {
        return StorageTree.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    private Tree<ResourceMeta> buildTree(Map<String,String> config) {
        if(null==config) {
            config = new HashMap<String, String>();
        }
        //base layer of storage
        TreeBuilder<ResourceMeta> builder = baseStorage(TreeBuilder.<ResourceMeta>builder());

        int storeIndex = 1;

        while (config.containsKey(getStorageConfigPrefix() + SEP + storeIndex + SEP + TYPE)) {
            configureStoragePlugin(builder, storeIndex, config);
            storeIndex++;
        }
        if (1 == storeIndex) {
            logger.debug("No storage plugins configured with prefix " + getStorageConfigPrefix());
        }
        builder = addLogger(builder,config);
        //apply default converters on top of storage
        builder = baseConverter(builder);

        //add plugin converters
        int converterIndex = 1;
        while (config.containsKey(getConverterConfigPrefix() + SEP + converterIndex + SEP + TYPE)) {
            builder = configureConverterPlugin(builder, converterIndex, config);
            converterIndex++;
        }
        if (1 == converterIndex) {
            logger.debug("No converter plugins configured with prefix " + getConverterConfigPrefix());
        }
        return builder.build();
    }

    /**
     * Apply base converters for metadata timestamps
     * @param builder
     * @return
     */
    private TreeBuilder<ResourceMeta> baseConverter(TreeBuilder<ResourceMeta> builder) {
        logger.debug("Configuring base converter: StorageTimestamperConverter" );
        logger.debug("Configuring base converter: KeyStorageLayer" );
        return builder.convert(
                new StorageConverterPluginAdapter("builtin:timestamp",
                        new StorageTimestamperConverter()
                )
        ).convert(
                new StorageConverterPluginAdapter("builtin:ssh-storage",
                        new KeyStorageLayer()
                ), PathUtil.asPath("/keys"));
    }

    /**
     * Append final listeners to the tree
     *
     * @param builder
     *
     * @return
     */
    private TreeBuilder<ResourceMeta> addLogger(TreeBuilder<ResourceMeta> builder, Map<String,String> config) {
        String loggerName= getLoggerName();
        if (null != config.get(LOGGER_NAME)) {
            loggerName = config.get(LOGGER_NAME);
        }
        if (null == loggerName) {
            loggerName = ORG_RUNDECK_STORAGE_EVENTS_LOGGER_NAME;
        }
        logger.debug("Add log4j logger for storage with name: " + loggerName);
        return builder.listen(new StorageLogger(loggerName));
    }

    /**
     * Set up the base storage layer for the tree
     *
     * @param builder builder
     *
     * @return builder
     */
    private TreeBuilder<ResourceMeta> baseStorage(TreeBuilder<ResourceMeta> builder) {
        //set base using file storage, could be overridden
        Map<String, String> config1 = expandConfig(getBaseStorageConfig());
        logger.debug("Default base storage provider: " + getBaseStorageType() + ", " +
                "config: " + config1);

        StoragePlugin base = loadPlugin(
                getBaseStorageType(),
                config1,
                storagePluginProviderService
        );
        if(null==base) {
            throw new IllegalArgumentException("Plugin could not be loaded: " + getBaseStorageType());
        }
        return builder.base(base);
    }

    private Map<String, String> stringStringMap(Map map) {
        HashMap<String, String> stringStringHashMap = new HashMap<String, String>();
        for (Object o : map.keySet()) {
            stringStringHashMap.put(o.toString(), map.get(o).toString());
        }
        return stringStringHashMap;
    }

    /**
     * Configure converter plugins for the builder
     *
     * @param builder     builder
     * @param index       given index
     * @param configProps configuration properties
     *
     * @return builder
     */
    private TreeBuilder<ResourceMeta> configureConverterPlugin(TreeBuilder<ResourceMeta> builder, int index,
            Map<String, String> configProps) {
        String pref1 = getConverterConfigPrefix() + SEP + index;
        String pluginType = configProps.get(pref1 + SEP + TYPE);
        String pathProp = pref1 + SEP + PATH;
        String selectorProp = pref1 + SEP + RESOURCE_SELECTOR;
        String path = configProps.get(pathProp);
        String selector = configProps.get(selectorProp);
        if (null == path && null == selector) {
            throw new IllegalArgumentException("Converter plugin [" + index + "] specified by " + (pref1) + " MUST " +
                    "define one of: " +
                    pathProp + " OR " + selectorProp);
        }

        Map<String, String> config = subPropertyMap(pref1 + SEP + CONFIG + SEP, configProps);
        config = expandConfig(config);
        logger.debug("Add Converter[" + index + "]:"
                + (null != path ? path : "/")
                + "[" + (null != selector ? selector : "*") + "]"
                + " " + pluginType + ", config: " + config);


        return buildConverterPlugin(builder, pluginType, path, selector, config);
    }

    /**
     * Append a converter plugin to the tree builder
     *
     * @param builder    builder
     * @param pluginType converter plugin type
     * @param path       path
     * @param selector   metadata selector
     * @param config     plugin config data
     *
     * @return builder
     */
    private TreeBuilder<ResourceMeta> buildConverterPlugin(TreeBuilder<ResourceMeta> builder, String pluginType,
            String path, String selector, Map<String, String> config) {
        StorageConverterPlugin converterPlugin = loadPlugin(
                pluginType,
                config,
                storageConverterPluginProviderService
        );
        //convert tree under the subpath if specified, AND matching the selector if specified
        return builder.convert(
                new StorageConverterPluginAdapter(pluginType,converterPlugin),
                null != path ? PathUtil.asPath(path.trim()) : null,
                null != selector ? PathUtil.<ResourceMeta>resourceSelector(selector) : null
        );
    }


    /**
     * Extract a map of the property values starting with the given prefix
     *
     * @param configPrefix  prefix
     * @param propertiesMap input
     *
     * @return map
     */
    private Map<String, String> subPropertyMap(String configPrefix, Map propertiesMap) {
        Map<String, String> config = new HashMap<String, String>();
        for (Object o : propertiesMap.keySet()) {
            String key = (String) o;
            if (key.startsWith(configPrefix)) {
                String conf = key.substring(configPrefix.length());
                config.put(conf, propertiesMap.get(key).toString());
            }
        }
        return config;
    }

    /**
     * Configures storage plugins with the builder
     *
     * @param builder     builder
     * @param index       current prop index
     * @param configProps configuration properties
     */
    private void configureStoragePlugin(TreeBuilder<ResourceMeta> builder, int index, Map<String, String> configProps) {
        String pref1 = getStorageConfigPrefix() + SEP + index;
        String pluginType = configProps.get(pref1 + SEP + TYPE);
        String path = configProps.get(pref1 + SEP + PATH);
        boolean removePathPrefix = Boolean.parseBoolean(configProps.get(pref1 + SEP +
                REMOVE_PATH_PREFIX));

        Map<String, String> config = subPropertyMap(pref1 + SEP + CONFIG + SEP, configProps);
        config = expandConfig(config);
        Tree<ResourceMeta> resourceMetaTree = loadPlugin(
                pluginType,
                config,
                storagePluginProviderService
        );
        if (index == 1 && PathUtil.isRoot(path)) {
            logger.debug("New base Storage[" + index + "]:" + path + " " + pluginType + ", config: " + config);
            builder.base(resourceMetaTree);
        } else {
            logger.debug("Subtree Storage[" + index + "]:" + path + " " + pluginType + ", config: " + config);
            builder.subTree(PathUtil.asPath(path.trim()), resourceMetaTree, !removePathPrefix);
        }
    }

    /**
     * Expand embedded framework property references in the map values
     *
     * @param map map
     *
     * @return expanded map
     */
    private Map<String, String> expandConfig(Map<String, String> map) {
        return expandAllProperties(map, rundeckFramework.getPropertyLookup().getPropertiesMap());
    }

    private Map<String, String> expandAllProperties(Map<String, String> source, Map values) {
        HashMap<String, String> result = new HashMap<String, String>();
        for (String s : source.keySet()) {
            result.put(s, PropertyUtil.expand(source.get(s), values));
        }
        return result;
    }

    private <T> T loadPlugin(String pluginType, Map<String, String> config,
            PluggableProviderService<T> service) {
        ConfiguredPlugin<T> configured = getPluginRegistry().configurePluginByName(pluginType, service,
                rundeckFramework, null, config);
        if (null == configured) {
            throw new IllegalArgumentException(service.getName() + " Plugin named \"" + pluginType + "\" could not be" +
                    " " +
                    "loaded");
        }
        return configured.getInstance();
    }

    public String getStorageConfigPrefix() {
        return storageConfigPrefix;
    }

    public void setStorageConfigPrefix(String storageConfigPrefix) {
        this.storageConfigPrefix = storageConfigPrefix;
    }

    public String getConverterConfigPrefix() {
        return converterConfigPrefix;
    }

    public void setConverterConfigPrefix(String converterConfigPrefix) {
        this.converterConfigPrefix = converterConfigPrefix;
    }

    public PluginRegistry getPluginRegistry() {
        return pluginRegistry;
    }

    public void setPluginRegistry(PluginRegistry pluginRegistry) {
        this.pluginRegistry = pluginRegistry;
    }

    public StorageConverterPluginProviderService getStorageConverterPluginProviderService() {
        return storageConverterPluginProviderService;
    }

    public void setStorageConverterPluginProviderService(StorageConverterPluginProviderService
            storageConverterPluginProviderService) {
        this.storageConverterPluginProviderService = storageConverterPluginProviderService;
    }

    public StoragePluginProviderService getStoragePluginProviderService() {
        return storagePluginProviderService;
    }

    public void setStoragePluginProviderService(StoragePluginProviderService
            storagePluginProviderService) {
        this.storagePluginProviderService = storagePluginProviderService;
    }

    public String getBaseStorageType() {
        return baseStorageType;
    }

    public void setBaseStorageType(String baseStorageType) {
        this.baseStorageType = baseStorageType;
    }

    public Map<String, String> getBaseStorageConfig() {
        return baseStorageConfig;
    }

    public void setBaseStorageConfig(Map<String, String> baseStorageConfig) {
        this.baseStorageConfig = baseStorageConfig;
    }

    public String getLoggerName() {
        return loggerName;
    }

    public void setLoggerName(String loggerName) {
        this.loggerName = loggerName;
    }

    public Map<String, String> getConfiguration() {
        return configuration;
    }

    public void setConfiguration(Map<String, String> configuration) {
        this.configuration = configuration;
    }
}
TOP

Related Classes of com.dtolabs.rundeck.server.storage.StorageTreeFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.