Package com.dtolabs.rundeck.core.plugins

Source Code of com.dtolabs.rundeck.core.plugins.ScriptPluginProviderLoader

/*
* Copyright 2011 DTO Solutions, Inc. (http://dtosolutions.com)
*
*  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.
*/

/*
* ScriptFileProviderLoader.java
*
* User: Greg Schueler <a href="mailto:greg@dtosolutions.com">greg@dtosolutions.com</a>
* Created: 4/13/11 10:07 AM
*
*/
package com.dtolabs.rundeck.core.plugins;

import com.dtolabs.rundeck.core.execution.script.ScriptfileUtils;
import com.dtolabs.rundeck.core.execution.service.ProviderLoaderException;
import com.dtolabs.rundeck.core.plugins.metadata.ProviderDef;
import com.dtolabs.rundeck.core.plugins.metadata.PluginMeta;
import com.dtolabs.rundeck.core.utils.FileUtils;
import com.dtolabs.rundeck.core.utils.ZipUtil;
import com.dtolabs.rundeck.core.utils.cache.FileCache;
import org.apache.log4j.Logger;
import org.yaml.snakeyaml.JavaBeanLoader;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
* ScriptPluginProviderLoader can load a provider instance for a service from a script plugin zip file.
*
* @author Greg Schueler <a href="mailto:greg@dtosolutions.com">greg@dtosolutions.com</a>
*/
class ScriptPluginProviderLoader implements ProviderLoader, FileCache.Expireable {

    private static final Logger log = Logger.getLogger(ScriptPluginProviderLoader.class.getName());
    public static final String SCRIPT_PLUGIN_VERSION = "1.0";
    private final File file;
    final File cachedir;
    /**
     * Dir of expanded zip contents
     */
    private File fileExpandedDir;
    /**
     * Metadata from the plugin.yaml file
     */
    private PluginMeta metadata;
    /**
     * cache of ident to scriptplugin def mapping
     */
    private Map<ProviderIdent, ScriptPluginProvider> pluginProviderDefs =
        new HashMap<ProviderIdent, ScriptPluginProvider>();

    public ScriptPluginProviderLoader(final File file, final File cachedir) {
        this.file = file;
        this.cachedir = cachedir;
    }

    /**
     * Load a provider instance for the service by name
     */
    public synchronized <T> T load(final PluggableService<T> service, final String providerName) throws
        ProviderLoaderException {
        if (!service.isScriptPluggable()) {
            return null;
        }
        final ProviderIdent ident = new ProviderIdent(service.getName(), providerName);

        if (null == pluginProviderDefs.get(ident)) {
            //look for plugin def
            final PluginMeta pluginMeta;
            try {
                pluginMeta = getPluginMeta();
            } catch (IOException e) {
                throw new ProviderLoaderException(e, service.getName(), providerName);
            }
            if (null == pluginMeta) {
                throw new ProviderLoaderException("Unable to load plugin metadata for file: " + file, service.getName(),
                    providerName);
            }
            for (final ProviderDef pluginDef : pluginMeta.getPluginDefs()) {
                if (matchesProvider(ident, pluginDef)) {
                    final ScriptPluginProvider provider;
                    try {
                        provider = getPlugin(file, pluginDef, ident);
                    } catch (PluginException e) {
                        throw new ProviderLoaderException(e, service.getName(), providerName);
                    }
                    pluginProviderDefs.put(ident, provider);
                    break;
                }
            }
        }
        final ScriptPluginProvider scriptPluginProvider = pluginProviderDefs.get(ident);

        if (null != scriptPluginProvider) {
            try {
                return service.createScriptProviderInstance(scriptPluginProvider);
            } catch (PluginException e) {
                throw new ProviderLoaderException(e, service.getName(), providerName);
            }
        }
        return null;
    }


    /**
     * Get the plugin metadata, loading from the file if necessary
     *
     * @return loaded metadata or null if not found
     *
     * @throws IOException if an error occurs trying to load from the file
     */
    private PluginMeta getPluginMeta() throws IOException {
        if (null != metadata) {
            return metadata;
        }
        metadata = loadMeta(file);
        return metadata;
    }

    /**
     * Get the ScriptPluginProvider definition from the file for the given provider def and ident
     */
    private ScriptPluginProvider getPlugin(final File file, final ProviderDef pluginDef, final ProviderIdent ident) throws
        ProviderLoaderException, PluginException {
        if (null == fileExpandedDir) {
            final File dir;
            try {
                dir = expandScriptPlugin(file);
            } catch (IOException e) {
                throw new ProviderLoaderException(e, ident.getService(), ident.getProviderName());
            }
            fileExpandedDir = dir;
            final File script = new File(fileExpandedDir, pluginDef.getScriptFile());
            //set executable bit for script-file of the provider
            try {
                ScriptfileUtils.setExecutePermissions(script);
            } catch (IOException e) {
                log.warn("Unable to set executable bit for script file: " + script + ": " + e.getMessage());
            }
            debug("expanded plugin dir! " + fileExpandedDir);
        } else {
            debug("expanded plugin dir: " + fileExpandedDir);
        }

        final File script = new File(fileExpandedDir, pluginDef.getScriptFile());
        if (!script.exists() || !script.isFile()) {
            throw new PluginException("Script file was not found: " + script.getAbsolutePath());
        }
        return new ScriptPluginProviderImpl(pluginDef, file, fileExpandedDir);
    }

    /**
     * Return true if the ident matches the provider def metadata
     */
    private boolean matchesProvider(final ProviderIdent ident, final ProviderDef pluginDef) {
        return ident.getService().equals(pluginDef.getService()) && ident.getProviderName().equals(pluginDef.getName());
    }

    /**
     * Return true if the plugin file can loade a provider for the ident
     */
    public synchronized boolean isLoaderFor(final ProviderIdent ident) {

        final PluginMeta pluginMeta;
        try {
            pluginMeta = getPluginMeta();
        } catch (IOException e) {
            log.warn("Unable to load file meta: " + e.getMessage());
            return false;
        }
        if (null == pluginMeta) {
            return false;
        }
        for (final ProviderDef pluginDef : pluginMeta.getPluginDefs()) {
            if (matchesProvider(ident, pluginDef)) {
                return true;
            }
        }
        return false;
    }

    public List<ProviderIdent> listProviders() {
        final ArrayList<ProviderIdent> providerIdents = new ArrayList<ProviderIdent>();
        PluginMeta pluginMeta=null;
        try {
            pluginMeta = getPluginMeta();
        } catch (IOException e) {
            debug("Unable to load file meta: " + e.getMessage());
        }
        if (null == pluginMeta) {
            return providerIdents;
        }
        for (final ProviderDef pluginDef : pluginMeta.getPluginDefs()) {
            providerIdents.add(new ProviderIdent(pluginDef.getService(), pluginDef.getName()));
        }
        return providerIdents;
    }


    /**
     * Get plugin metadatat from a zip file
     */
    static PluginMeta loadMeta(final File jar) throws IOException {
        FileInputStream fileInputStream = new FileInputStream(jar);
        try{
            final ZipInputStream zipinput = new ZipInputStream(fileInputStream);
            final PluginMeta metadata = ScriptPluginProviderLoader.loadMeta(jar, zipinput);
            return metadata;
        }finally {
            fileInputStream.close();
        }
    }

    /**
     * Load plugin metadata for a file and zip inputstream
     * @param jar the file
     * @param zipinput zip input stream
     * @return loaded metadata, or null if it is invalid or not found
     */
    static PluginMeta loadMeta(final File jar, final ZipInputStream zipinput) throws IOException {
        final String basename = basename(jar);
        PluginMeta metadata = null;
        boolean topfound = false;
        boolean found = false;
        boolean dirfound = false;
        ZipEntry nextEntry = zipinput.getNextEntry();
        while (null != nextEntry) {
            if (!topfound && nextEntry.getName().startsWith(basename + "/")) {
                topfound = true;
            }
            if (!dirfound && (nextEntry.getName().startsWith(basename + "/contents/")
                              || nextEntry.isDirectory() && nextEntry.getName().equals(
                basename + "/contents"))) {

//                debug("Found contents dir: " + nextEntry.getName());
                dirfound = true;
            }
            if (!found && !nextEntry.isDirectory() && nextEntry.getName().equals(basename + "/plugin.yaml")) {
//                debug("Found metadata: " + nextEntry.getName());
                try {
                    metadata = loadMetadataYaml(zipinput);
                    found = true;
                } catch (Throwable e) {
                    log.error("Error parsing metadata file plugin.yaml: " + e.getMessage(), e);
                }
            }
            if (dirfound && found) {
                break;
            }
            nextEntry = zipinput.getNextEntry();
        }
        if (!topfound) {
            log.error("Plugin not loaded: Found no " + basename + "/ dir within file: " + jar.getAbsolutePath());
        }
        if (!found) {
            log.error("Plugin not loaded: Found no " + basename + "/plugin.yaml within: " + jar.getAbsolutePath());
        }
        if (!dirfound) {
            log.error("Plugin not loaded: Found no " + basename + "/contents dir within: " + jar.getAbsolutePath());
        }
        if (found && dirfound) {
            return metadata;
        }
        return null;
    }

    /**
     * return loaded yaml plugin metadata from the stream
     */
    static PluginMeta loadMetadataYaml(final InputStream stream) {
        final Yaml yaml = new Yaml();

        return yaml.loadAs(stream, PluginMeta.class);
    }

    /**
     * Return true if loaded metadata about the plugin file is valid.
     */
    static boolean validatePluginMeta(final PluginMeta pluginList, final File file) {
        boolean valid = true;
        if (null == pluginList.getName()) {
            log.error("name not found in metadata: " + file.getAbsolutePath());
            valid = false;
        }
        if (null == pluginList.getVersion()) {
            log.error("version not found in metadata: " + file.getAbsolutePath());
            valid = false;
        }
        if (null == pluginList.getRundeckPluginVersion()) {
            log.error("rundeckPluginVersion not found in metadata: " + file.getAbsolutePath());
            valid = false;
        } else if (!SCRIPT_PLUGIN_VERSION.equals(pluginList.getRundeckPluginVersion())) {
            log.error("rundeckPluginVersion: " + pluginList.getRundeckPluginVersion() + " is not supported: " + file
                    .getAbsolutePath());
            valid = false;
        }
        final List<ProviderDef> pluginDefs = pluginList.getPluginDefs();
        for (final ProviderDef pluginDef : pluginDefs) {
            try {
                validateProviderDef(pluginDef);
            } catch (PluginException e) {
                valid = false;
            }
        }
        return valid;
    }

    /**
     * Expand zip file into plugin cache dir
     *
     * @param file zip file
     *
     * @return cache dir for the contents of the plugin zip
     */
    private File expandScriptPlugin(final File file) throws IOException {
        if (!cachedir.exists()) {
            if (!cachedir.mkdirs()) {
                log.warn("Unable to create cache dir: " + cachedir.getAbsolutePath());
            }
        }
        final File jardir = getFileCacheDir();
        if (!jardir.exists()) {
            if (!jardir.mkdir()) {
                log.warn("Unable to create cache dir for plugin: " + jardir.getAbsolutePath());
            }
        }
        final String prefix = getFileBasename() + "/contents";

        debug("Expand zip " + file.getAbsolutePath() + " to dir: " + jardir + ", prefix: " + prefix);
        ZipUtil.extractZip(file.getAbsolutePath(), jardir, prefix, prefix + "/");

        return jardir;
    }

    /**
     * Remove any cache dir for the file
     */
    private synchronized boolean removeScriptPluginCache() {
        if (null != fileExpandedDir && fileExpandedDir.exists()) {
            debug("removeScriptPluginCache: " + fileExpandedDir);
            return FileUtils.deleteDir(fileExpandedDir);
        }
        return true;
    }

    /**
     * Basename of the file
     */
    String getFileBasename() {
        return basename(file);
    }

    /**
     * Get basename of a file
     */
    private static String basename(final File file) {
        final String name = file.getName();
        return name.substring(0, name.lastIndexOf("."));
    }

    /**
     * Get the cache dir for use for this file
     */
    File getFileCacheDir() {
        return new File(cachedir, getFileBasename());
    }


    /**
     * Validate provider def
     */
    private static void validateProviderDef(final ProviderDef pluginDef) throws PluginException {

        if (null == pluginDef.getPluginType() || "".equals(pluginDef.getPluginType())) {
            throw new PluginException("Script plugin missing plugin-type");
        }
        if ("script".equals(pluginDef.getPluginType())) {
            validateScriptProviderDef(pluginDef);
        } else {
            throw new PluginException("plugin missing has invalid plugin-type: " + pluginDef.getPluginType());
        }
    }

    /**
     * Validate script provider def
     */
    private static void validateScriptProviderDef(final ProviderDef pluginDef) throws PluginException {
        if (null == pluginDef.getName() || "".equals(pluginDef.getName())) {
            throw new PluginException("Script plugin missing name");
        }
        if (null == pluginDef.getService() || "".equals(pluginDef.getService())) {
            throw new PluginException("Script plugin missing service");
        }
        if (null == pluginDef.getScriptFile() || "".equals(pluginDef.getScriptFile())) {
            throw new PluginException("Script plugin missing script-file");
        }

        //make sure service is pluggable service and is script pluggable
        /* final FrameworkSupportService service = framework.getService(pluginDef.getService());
        if (!(service instanceof PluggableService)) {
            throw new PluginException(
                "Service '" + pluginDef.getService() + "' specified for script plugin '" + pluginDef.getName()
                + "' is not valid: unsupported");
        }
        final PluggableService pservice = (PluggableService) service;
        if (!pservice.isScriptPluggable()) {
            throw new PluginException(
                "Service '" + pluginDef.getService() + "' specified for script plugin '" + pluginDef.getName()
                + "' is not valid: unsupported");
        }*/
    }

    private static void debug(final String msg) {
        if (log.isDebugEnabled()) {
            log.debug(msg);
        }
    }

    /**
     * Expire the loader cache item
     */
    public void expire() {
        removeScriptPluginCache();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        ScriptPluginProviderLoader that = (ScriptPluginProviderLoader) o;

        if (cachedir != null ? !cachedir.equals(that.cachedir) : that.cachedir != null) {
            return false;
        }
        if (file != null ? !file.equals(that.file) : that.file != null) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result = file != null ? file.hashCode() : 0;
        result = 31 * result + (cachedir != null ? cachedir.hashCode() : 0);
        return result;
    }


    /**
     * Return the version string metadata value for the plugin file, or null if it is not available or could not
     * loaded
     * @param file
     * @return version string
     */
    static String getVersionForFile(final File file)  {
        try {
            final PluginMeta pluginMeta = loadMeta(file);
            return pluginMeta.getVersion();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
TOP

Related Classes of com.dtolabs.rundeck.core.plugins.ScriptPluginProviderLoader

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.