azkaban.webapp.TriggerPluginLoader.java Source code

Java tutorial

Introduction

Here is the source code for azkaban.webapp.TriggerPluginLoader.java

Source

/*
 * Copyright 2017 LinkedIn Corp.
 *
 * 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 azkaban.webapp;

import static azkaban.ServiceProvider.SERVICE_PROVIDER;

import azkaban.utils.FileIOUtils;
import azkaban.utils.Props;
import azkaban.utils.PropsUtils;
import azkaban.webapp.plugin.TriggerPlugin;
import java.io.File;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.velocity.app.VelocityEngine;
import org.mortbay.jetty.servlet.Context;

public class TriggerPluginLoader {

    private static final Logger log = Logger.getLogger(TriggerPluginLoader.class);
    private final String pluginPath;

    public TriggerPluginLoader(final Props props) {
        this.pluginPath = props.getString("trigger.plugin.dir", "plugins/triggers");
    }

    public Map<String, TriggerPlugin> loadTriggerPlugins(final Context root) {
        /*
         * TODO spyne: TriggerPluginLoader should not have any dependency on Azkaban Web Server
         **/
        final AzkabanWebServer azkabanWebServer = SERVICE_PROVIDER.getInstance(AzkabanWebServer.class);
        final File triggerPluginPath = new File(this.pluginPath);
        if (!triggerPluginPath.exists()) {
            return new HashMap<>();
        }

        final Map<String, TriggerPlugin> installedTriggerPlugins = new HashMap<>();
        final ClassLoader parentLoader = AzkabanWebServer.class.getClassLoader();
        final File[] pluginDirs = triggerPluginPath.listFiles();
        final ArrayList<String> jarPaths = new ArrayList<>();
        for (final File pluginDir : pluginDirs) {
            if (!pluginDir.exists()) {
                log.error("Error! Trigger plugin path " + pluginDir.getPath() + " doesn't exist.");
                continue;
            }

            if (!pluginDir.isDirectory()) {
                log.error("The plugin path " + pluginDir + " is not a directory.");
                continue;
            }

            // Load the conf directory
            final File propertiesDir = new File(pluginDir, "conf");
            Props pluginProps = null;
            if (propertiesDir.exists() && propertiesDir.isDirectory()) {
                final File propertiesFile = new File(propertiesDir, "plugin.properties");
                final File propertiesOverrideFile = new File(propertiesDir, "override.properties");

                if (propertiesFile.exists()) {
                    if (propertiesOverrideFile.exists()) {
                        pluginProps = PropsUtils.loadProps(null, propertiesFile, propertiesOverrideFile);
                    } else {
                        pluginProps = PropsUtils.loadProps(null, propertiesFile);
                    }
                } else {
                    log.error("Plugin conf file " + propertiesFile + " not found.");
                    continue;
                }
            } else {
                log.error("Plugin conf path " + propertiesDir + " not found.");
                continue;
            }

            final String pluginName = pluginProps.getString("trigger.name");
            final List<String> extLibClasspath = pluginProps.getStringList("trigger.external.classpaths",
                    (List<String>) null);

            final String pluginClass = pluginProps.getString("trigger.class");
            if (pluginClass == null) {
                log.error("Trigger class is not set.");
            } else {
                log.error("Plugin class " + pluginClass);
            }

            URLClassLoader urlClassLoader = null;
            final File libDir = new File(pluginDir, "lib");
            if (libDir.exists() && libDir.isDirectory()) {
                final File[] files = libDir.listFiles();

                final ArrayList<URL> urls = new ArrayList<>();
                for (int i = 0; i < files.length; ++i) {
                    try {
                        final URL url = files[i].toURI().toURL();
                        urls.add(url);
                    } catch (final MalformedURLException e) {
                        log.error(e);
                    }
                }
                if (extLibClasspath != null) {
                    for (final String extLib : extLibClasspath) {
                        try {
                            final File file = new File(pluginDir, extLib);
                            final URL url = file.toURI().toURL();
                            urls.add(url);
                        } catch (final MalformedURLException e) {
                            log.error(e);
                        }
                    }
                }

                urlClassLoader = new URLClassLoader(urls.toArray(new URL[urls.size()]), parentLoader);
            } else {
                log.error("Library path " + propertiesDir + " not found.");
                continue;
            }

            Class<?> triggerClass = null;
            try {
                triggerClass = urlClassLoader.loadClass(pluginClass);
            } catch (final ClassNotFoundException e) {
                log.error("Class " + pluginClass + " not found.");
                continue;
            }

            final String source = FileIOUtils.getSourcePathFromClass(triggerClass);
            log.info("Source jar " + source);
            jarPaths.add("jar:file:" + source);

            Constructor<?> constructor = null;
            try {
                constructor = triggerClass.getConstructor(String.class, Props.class, Context.class,
                        AzkabanWebServer.class);
            } catch (final NoSuchMethodException e) {
                log.error("Constructor not found in " + pluginClass);
                continue;
            }

            Object obj = null;
            try {
                obj = constructor.newInstance(pluginName, pluginProps, root, azkabanWebServer);
            } catch (final Exception e) {
                log.error(e);
            }

            if (!(obj instanceof TriggerPlugin)) {
                log.error("The object is not an TriggerPlugin");
                continue;
            }

            final TriggerPlugin plugin = (TriggerPlugin) obj;
            installedTriggerPlugins.put(pluginName, plugin);
        }

        // Velocity needs the jar resource paths to be set.
        final String jarResourcePath = StringUtils.join(jarPaths, ", ");
        log.info("Setting jar resource path " + jarResourcePath);
        final VelocityEngine ve = azkabanWebServer.getVelocityEngine();
        ve.addProperty("jar.resource.loader.path", jarResourcePath);

        return installedTriggerPlugins;
    }

}