Skip to content

Commit

Permalink
Refactor Alerter plugins feature. (azkaban#3217)
Browse files Browse the repository at this point in the history
  • Loading branch information
ypadron-in authored Jan 3, 2023
1 parent bfd36e2 commit f627f96
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 214 deletions.
141 changes: 70 additions & 71 deletions azkaban-common/src/main/java/azkaban/executor/AlerterHolder.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,119 +16,118 @@
package azkaban.executor;

import azkaban.alert.Alerter;
import azkaban.spi.AzkabanException;
import azkaban.utils.Emailer;
import azkaban.utils.FileIOUtils;
import azkaban.utils.PluginUtils;
import azkaban.utils.Props;
import azkaban.utils.PropsUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.io.File;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;

import java.util.function.BiConsumer;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
public class AlerterHolder {

private static final Logger logger = Logger.getLogger(AlerterHolder.class);
private Map<String, Alerter> alerters;
private static final Logger logger = LoggerFactory.getLogger(AlerterHolder.class);
private final Map<String, Alerter> alerters;

@Inject
public AlerterHolder(final Props props, final Emailer mailAlerter) {
try {
this.alerters = loadAlerters(props, mailAlerter);
} catch (final Exception ex) {
logger.error(ex);
this.alerters = new HashMap<>();
}
}

private Map<String, Alerter> loadAlerters(final Props props, final Emailer mailAlerter) {
final Map<String, Alerter> allAlerters = new HashMap<>();
// load built-in alerters
// Load built-in alerters.
allAlerters.put("email", mailAlerter);
// load all plugin alerters
final String pluginDir = props.getString("alerter.plugin.dir", "plugins/alerter");
allAlerters.putAll(loadPluginAlerters(pluginDir));
return allAlerters;
// Load additional alerter plugins if any.
final String customAlertersBaseDir = props.getString("alerter.plugin.dir", "plugins/alerter");
allAlerters.putAll(loadCustomAlerters(customAlertersBaseDir));
this.alerters = allAlerters;
logger.info("Alerter plugins loaded: {}", this.alerters.keySet());
}

private Map<String, Alerter> loadPluginAlerters(final String pluginPath) {
final File alerterPluginPath = new File(pluginPath);
if (!alerterPluginPath.exists()) {
private Map<String, Alerter> loadCustomAlerters(final String alerterPluginsRootPath) {
final File pluginsRootPath = new File(alerterPluginsRootPath);
if (!pluginsRootPath.exists()) {
return Collections.<String, Alerter>emptyMap();
}

final Map<String, Alerter> installedAlerterPlugins = new HashMap<>();
final ClassLoader parentLoader = getClass().getClassLoader();
final File[] pluginDirs = alerterPluginPath.listFiles();
final ArrayList<String> jarPaths = new ArrayList<>();
final File[] pluginDirs = pluginsRootPath.listFiles();

for (final File pluginDir : pluginDirs) {
// load plugin properties
final Props pluginProps = PropsUtils.loadPluginProps(pluginDir);
if (pluginProps == null) {
continue;
}

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

final String pluginClass = pluginProps.getString("alerter.class");
if (pluginClass == null) {
logger.error("Alerter class is not set.");
continue;
} else {
logger.info("Plugin class " + pluginClass);
try {
loadCustomAlerter(installedAlerterPlugins, parentLoader, pluginDir);
} catch (final Exception e) {
logger.error(String.format("Failed to load alerter plugin in '%s'.", pluginDir), e);
}
}
return installedAlerterPlugins;
}

Class<?> alerterClass =
PluginUtils.getPluginClass(pluginClass, pluginDir, extLibClassPaths, parentLoader);
private void loadCustomAlerter(final Map<String, Alerter> installedAlerterPlugins,
final ClassLoader parentLoader, final File pluginDir) {
// Load plugin properties.
final Props pluginProps = PropsUtils.loadPluginProps(pluginDir);
if (pluginProps == null) {
throw new AzkabanException("Plugin config properties could not be loaded.");
}

if (alerterClass == null) {
continue;
}
final String pluginName = pluginProps.getString("alerter.name", "").trim();
if (pluginName.isEmpty()) {
throw new AzkabanException("Alerter name is required.");
}

final String source = FileIOUtils.getSourcePathFromClass(alerterClass);
logger.info("Source jar " + source);
jarPaths.add("jar:file:" + source);
final List<String> extLibClassPaths = pluginProps.getStringList(
"alerter.external.classpaths", (List<String>) null);

Constructor<?> constructor = null;
try {
constructor = alerterClass.getConstructor(Props.class);
} catch (final NoSuchMethodException e) {
logger.error("Constructor not found in " + pluginClass);
continue;
}
final String pluginClass = pluginProps.getString("alerter.class", "").trim();
if (pluginClass.isEmpty()) {
throw new AzkabanException("Alerter class is required.");
}

Object obj = null;
try {
obj = constructor.newInstance(pluginProps);
} catch (final Exception e) {
logger.error(e);
}
final Class<?> alerterClass =
PluginUtils.getPluginClass(pluginClass, pluginDir, extLibClassPaths, parentLoader);
if (alerterClass == null) {
throw new AzkabanException(
String.format("Alerter class '%s' could not be loaded.", pluginClass));
}
logger.info("Loaded alerter class '{}' from '{}'.", pluginClass,
FileIOUtils.getSourcePathFromClass(alerterClass));

if (!(obj instanceof Alerter)) {
logger.error("The object is not an Alerter");
continue;
}
Constructor<?> constructor = null;
try {
constructor = alerterClass.getConstructor(Props.class);
} catch (final NoSuchMethodException e) {
throw new AzkabanException("Alerter class constructor wasn't found.", e);
}

final Alerter plugin = (Alerter) obj;
installedAlerterPlugins.put(pluginName, plugin);
final Object obj;
try {
obj = constructor.newInstance(pluginProps);
} catch (final Exception e) {
throw new AzkabanException(String.format("Alerter class '%s' could not be instantiated.",
pluginClass), e);
}

return installedAlerterPlugins;
if (!(obj instanceof Alerter)) {
throw new AzkabanException("Instantiated object is not an Alerter.");
}
installedAlerterPlugins.put(pluginName, (Alerter) obj);
}

public Alerter get(final String alerterType) {
return this.alerters.get(alerterType);
}

public void forEach(final BiConsumer<String, Alerter> consumer) {
this.alerters.forEach(consumer);
}
}
Loading

0 comments on commit f627f96

Please sign in to comment.