Skip to content

Commit

Permalink
NMS-9290: refactoring DroolsCorrelationEngineBuilder to support hot-a…
Browse files Browse the repository at this point in the history
…dd and hot-delete

The spring initialization was modified in order to support the on-demand
addition of new engine directories, and the on-demand removal of engine
directories. For this purpose, a reloadDaemonConfig is required, using
'DroolsCorrelationEngine' for the 'daemonName'.

To update an existing engine, the intial design will be used, which is,
use 'DroolsCorrelationEngine-XXX' where XXX is the name of the engine
(or RuleSet) for the daemonName used on the reloadDaemonConfig event.
  • Loading branch information
agalue committed Jul 20, 2017
1 parent 492136d commit 0d71c06
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 43 deletions.
Expand Up @@ -46,6 +46,7 @@ public ConfigFileApplicationContext(Resource basePath, final String configFileLo
super(parent);
m_resource = basePath;
m_configFileLocation = configFileLocation;
LOG.debug("ConfigFileApplicationContext: initializing using basePath={}, configFileLocation={}", m_resource, m_configFileLocation);
refresh();
}

Expand Down
Expand Up @@ -30,22 +30,32 @@

import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.opennms.core.xml.JaxbUtils;
import org.opennms.netmgt.correlation.CorrelationEngine;
import org.opennms.netmgt.correlation.CorrelationEngineRegistrar;
import org.opennms.netmgt.correlation.drools.config.EngineConfiguration;
import org.opennms.netmgt.correlation.drools.config.RuleSet;
import org.opennms.netmgt.events.api.EventConstants;
import org.opennms.netmgt.events.api.EventIpcManager;
import org.opennms.netmgt.events.api.EventListener;
import org.opennms.netmgt.events.api.EventProxyException;
import org.opennms.netmgt.model.events.EventBuilder;
import org.opennms.netmgt.xml.event.Event;
import org.opennms.netmgt.xml.event.Parm;
import org.springframework.beans.PropertyEditorRegistrySupport;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
Expand All @@ -58,7 +68,7 @@
* @author <a href="mailto:brozow@opennms.org">Mathew Brozowski</a>
* @version $Id: $
*/
public class DroolsCorrelationEngineBuilder extends PropertyEditorRegistrySupport implements InitializingBean, ApplicationListener<ApplicationEvent> {
public class DroolsCorrelationEngineBuilder extends PropertyEditorRegistrySupport implements InitializingBean, EventListener {
private static final Logger LOG = LoggerFactory.getLogger(DroolsCorrelationEngineBuilder.class);

public static final String PLUGIN_CONFIG_FILE_NAME = "drools-engine.xml";
Expand All @@ -77,19 +87,37 @@ public void readConfig() {
}

public CorrelationEngine[] constructEngines(ApplicationContext appContext, EventIpcManager eventIpcManager, MetricRegistry metricRegistry) {
LOG.info("Creating drools engins for configuration {}.", m_configResource);
LOG.info("Creating drools engines for configuration {}.", m_configResource);

return m_configuration.constructEngines(m_configResource, appContext, eventIpcManager, metricRegistry);
}

public List<RuleSet> getRuleSets() {
if (m_configuration == null) return Collections.emptyList();
return m_configuration.getRuleSetCollection();
}

public Resource getConfigResource() {
return m_configResource;
}
}

// injected
@javax.annotation.Resource(name="droolsCorrelationEngineBuilderConfigurationDirectory")
private File m_configDirectory;
@javax.annotation.Resource(name="droolsCorrelationEngineBuilderConfigurationResource")
private Resource m_configResource;
@Autowired
@Qualifier("eventIpcManager")
private EventIpcManager m_eventIpcManager;
@Autowired
@Qualifier("correlator")
private CorrelationEngineRegistrar m_correlator;
@Autowired
@Qualifier("metricRegistry")
private MetricRegistry m_metricRegistry;
@Autowired
private ApplicationContext m_appContext;

// built
private PluginConfiguration[] m_pluginConfigurations;
Expand Down Expand Up @@ -123,17 +151,19 @@ public void afterPropertiesSet() throws Exception {
assertSet(m_eventIpcManager, "eventIpcManager");
assertSet(m_correlator, "correlator");
assertSet(m_metricRegistry, "metricRegistry");
assertSet(m_appContext, "applicationContext");

Assert.state(!m_configDirectory.exists() || m_configDirectory.isDirectory(), m_configDirectory+" must be a directory!");

m_eventIpcManager.addEventListener(this, EventConstants.RELOAD_DAEMON_CONFIG_UEI);
readConfiguration();
registerEngines();
}

private void registerEngines(final ApplicationContext appContext) {
private void registerEngines() {
for(PluginConfiguration pluginConfig : m_pluginConfigurations) {
m_correlator.addCorrelationEngines(pluginConfig.constructEngines(appContext, m_eventIpcManager, m_metricRegistry));
m_correlator.addCorrelationEngines(pluginConfig.constructEngines(m_appContext, m_eventIpcManager, m_metricRegistry));
}

}

/**
Expand Down Expand Up @@ -238,16 +268,72 @@ public boolean accept(File file) {
return pluginDirs;
}

/** {@inheritDoc} */
@Override
public void onApplicationEvent(final ApplicationEvent appEvent) {
if (appEvent instanceof ContextRefreshedEvent) {
final ApplicationContext appContext = ((ContextRefreshedEvent)appEvent).getApplicationContext();
if (!(appContext instanceof ConfigFileApplicationContext)) {
registerEngines(appContext);

@Override
public String getName() {
return "DroolsCorrelationEngine";
}

@Override
public void onEvent(Event e) {
LOG.warn("{} received an event: {}", getName(), e.getUei());
// This handles only the addition of new engines and the removal of existing engines.
// For updating existing engines, use the appropriate value for the daemonName
if (e.getUei().equals(EventConstants.RELOAD_DAEMON_CONFIG_UEI)) {
List<Parm> parmCollection = e.getParmCollection();
for (Parm parm : parmCollection) {
String parmName = parm.getParmName();
if(EventConstants.PARM_DAEMON_NAME.equals(parmName)) {
if (parm.getValue() == null || parm.getValue().getContent() == null) {
LOG.warn("The daemonName parameter has no value, ignoring.");
return;
}
if (parm.getValue().getContent().equals(getName())) {
LOG.info("Analyzing directory {} to add/remove drools engines...", m_configDirectory);
EventBuilder ebldr = null;
try {
final PluginConfiguration[] newPlugins = locatePluginConfigurations();
final List<RuleSet> newEngines = Arrays.stream(newPlugins).peek(p -> p.readConfig()).flatMap(pc -> pc.getRuleSets().stream()).collect(Collectors.toList());
final List<RuleSet> currentEngines = Arrays.stream(m_pluginConfigurations).flatMap(pc -> pc.getRuleSets().stream()).collect(Collectors.toList());
LOG.debug("Current engines: {}", currentEngines);
LOG.debug("New engines: {}", newEngines);
// Find old engines to remove
currentEngines.stream().filter(en -> !newEngines.contains(en)).forEach(en -> {
LOG.warn("Deleting engine {}", en);
Optional<CorrelationEngine> correlator = m_correlator.getEngines().stream().filter(c -> c.getName().equals(en.getName())).findFirst();
if (correlator.isPresent()) {
correlator.get().tearDown();
m_correlator.getEngines().remove(correlator);
}
});
// Find new engines to add
newEngines.stream().filter(en -> !currentEngines.contains(en)).forEach(en -> {
LOG.warn("Adding engine {}", en);
Arrays.stream(newPlugins).filter(p -> p.getRuleSets().contains(en)).findFirst().ifPresent(p -> {
m_correlator.addCorrelationEngine(en.constructEngine(p.getConfigResource(), m_appContext, m_eventIpcManager, m_metricRegistry));
});
});
m_pluginConfigurations = newPlugins;
ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_SUCCESSFUL_UEI, getName());
ebldr.addParam(EventConstants.PARM_DAEMON_NAME, getName());
} catch (Exception ex) {
LOG.error("Cannot process reloadDaemonConfig", ex);
ebldr = new EventBuilder(EventConstants.RELOAD_DAEMON_CONFIG_FAILED_UEI, getName());
ebldr.addParam(EventConstants.PARM_DAEMON_NAME, getName());
ebldr.addParam(EventConstants.PARM_REASON, ex.getMessage());
} finally {
if (ebldr != null)
try {
m_eventIpcManager.send(ebldr.getEvent());
} catch (EventProxyException epx) {
LOG.error("Can't send reloadDaemonConfig status event", epx);
}
}
return;
}
}
}
}

}

}
Expand Up @@ -297,7 +297,7 @@ public boolean equals(Object obj) {

public CorrelationEngine[] constructEngines(Resource basePath, ApplicationContext appContext, EventIpcManager eventIpcManager, MetricRegistry metricRegistry) {

LOG.info("Creating drools engins for configuration {}.", basePath);
LOG.info("Creating drools engines for configuration {}.", basePath);

List<CorrelationEngine> engineList = new ArrayList<CorrelationEngine>();
for (final RuleSet ruleSet : getRuleSet()) {
Expand All @@ -308,4 +308,14 @@ public CorrelationEngine[] constructEngines(Resource basePath, ApplicationContex
return engineList.toArray(new CorrelationEngine[0]);
}

public CorrelationEngine constructEngine(Resource basePath, ApplicationContext appContext, EventIpcManager eventIpcManager, MetricRegistry metricRegistry, String engineName) {
for (final RuleSet ruleSet : getRuleSet()) {
if (ruleSet.getName().equals(engineName)) {
LOG.debug("Constucting engine for ruleset {} in configuration {}.", ruleSet.getName(), basePath);
return ruleSet.constructEngine(basePath, appContext, eventIpcManager, metricRegistry);
}
}
return null;
}

}
Expand Up @@ -785,4 +785,8 @@ public String getConfigLocation() {
return PropertiesUtils.substitute(getAppContext(), System.getProperties());
}

@Override
public String toString() {
return "RuleSet[" + getName() + "]";
}
}
@@ -1,10 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
">
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
">

<context:annotation-config />

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
Expand All @@ -28,14 +31,6 @@
<constructor-arg value="${opennms.home}/etc/drools-engine.d" />
</bean>

<bean id="droolsCorrelationEngineBuilder" class="org.opennms.netmgt.correlation.drools.DroolsCorrelationEngineBuilder">
<property name="eventIpcManager" ref="eventIpcManager" />
<property name="metricRegistry" ref="metricRegistry" />
<property name="correlationEngineRegistrar" ref="correlator" />
<property name="configurationResource" ref="droolsCorrelationEngineBuilderConfigurationResource"/>
<property name="configurationDirectory" ref="droolsCorrelationEngineBuilderConfigurationDirectory"/>
</bean>

<bean id="metricRegistry" class="com.codahale.metrics.MetricRegistry" />

<bean id="metricRegistryJmxReporterBuilder" class="com.codahale.metrics.JmxReporter" factory-method="forRegistry">
Expand All @@ -52,4 +47,5 @@
init-method="start"
destroy-method="stop" />

<bean id="droolsCorrelationEngineBuilder" class="org.opennms.netmgt.correlation.drools.DroolsCorrelationEngineBuilder"/>
</beans>
@@ -1,7 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
">

<context:annotation-config />

<bean id="droolsCorrelationEngineBuilderConfigurationResource" class="java.lang.String">
<constructor-arg value="file:src/test/opennms-home/etc/drools-engine.xml" />
Expand All @@ -11,16 +17,9 @@
<constructor-arg value="file:src/test/opennms-home/etc/drools-engine.d" />
</bean>

<bean id="droolsCorrelationEngineBuilder" class="org.opennms.netmgt.correlation.drools.DroolsCorrelationEngineBuilder">
<property name="eventIpcManager" ref="eventIpcManager" />
<property name="metricRegistry" ref="metricRegistry" />
<property name="correlationEngineRegistrar" ref="correlator" />
<property name="configurationResource" ref="droolsCorrelationEngineBuilderConfigurationResource"/>
<property name="configurationDirectory" ref="droolsCorrelationEngineBuilderConfigurationDirectory"/>
</bean>

<bean name="correlator" class="org.opennms.netmgt.correlation.drools.MockCorrelator" />

<bean id="metricRegistry" class="com.codahale.metrics.MetricRegistry" />

<bean id="droolsCorrelationEngineBuilder" class="org.opennms.netmgt.correlation.drools.DroolsCorrelationEngineBuilder"/>
</beans>
Expand Up @@ -106,7 +106,7 @@ private void handleReloadEvent(Event e) {
List<Parm> parmCollection = e.getParmCollection();
for (Parm parm : parmCollection) {
String parmName = parm.getParmName();
if("daemonName".equals(parmName)) {
if(EventConstants.PARM_DAEMON_NAME.equals(parmName)) {
if (parm.getValue() == null || parm.getValue().getContent() == null) {
LOG.warn("The daemonName parameter has no value, ignoring.");
return;
Expand Down

0 comments on commit 0d71c06

Please sign in to comment.