Java tutorial
/* * Copyright 2018 StreamSets Inc. * * 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 com.streamsets.datacollector.antennadoctor.engine; import com.google.common.collect.ImmutableList; import com.streamsets.datacollector.antennadoctor.bean.AntennaDoctorRuleBean; import com.streamsets.datacollector.antennadoctor.engine.context.AntennaDoctorContext; import com.streamsets.datacollector.antennadoctor.engine.context.AntennaDoctorStageContext; import com.streamsets.datacollector.antennadoctor.engine.jexl.SdcJexl; import com.streamsets.datacollector.antennadoctor.engine.jexl.StageIssueJexl; import com.streamsets.datacollector.util.Version; import com.streamsets.pipeline.api.AntennaDoctorMessage; import com.streamsets.pipeline.api.ErrorCode; import com.streamsets.pipeline.lib.el.CollectionEL; import com.streamsets.pipeline.lib.el.FileEL; import org.apache.commons.jexl3.JexlBuilder; import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlEngine; import org.apache.commons.jexl3.JexlException; import org.apache.commons.jexl3.JxltEngine; import org.apache.commons.jexl3.MapContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.StringWriter; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Main computing engine for Antenna Doctor. */ public class AntennaDoctorEngine { private static final Logger LOG = LoggerFactory.getLogger(AntennaDoctorEngine.class); /** * Rules that will be used to classify issues. */ private final List<RuntimeRule> rules; /** * Main evaluation engine. */ private final JexlEngine engine; /** * Templating engine for messages. */ private final JxltEngine templateEngine; public AntennaDoctorEngine(AntennaDoctorContext context, List<AntennaDoctorRuleBean> rules) { ImmutableList.Builder<RuntimeRule> builder = ImmutableList.builder(); Map<String, Object> namespaces = new HashMap<>(); namespaces.put("collection", CollectionEL.class); namespaces.put("file", FileEL.class); // Main engine used to evaluate all expressions and templates engine = new JexlBuilder().cache(512).strict(true).silent(false).debug(true).namespaces(namespaces) .create(); templateEngine = engine.createJxltEngine(); // Context for preconditions JexlContext jexlContext = new MapContext(); jexlContext.set("version", new Version(context.getBuildInfo().getVersion())); jexlContext.set("sdc", new SdcJexl(context)); // Evaluate each rule to see if it's applicable to this runtime (version, stage libs, ...) for (AntennaDoctorRuleBean ruleBean : rules) { LOG.trace("Loading rule {}", ruleBean.getUuid()); // We're running in SDC and currently only in STAGE 'mode', other modes will be added later if (ruleBean.getEntity() == null || !ruleBean.getEntity().isOneOf(AntennaDoctorRuleBean.Entity.STAGE, AntennaDoctorRuleBean.Entity.REST)) { continue; } // Reset the variables that the rule is keeping jexlContext.set("context", new HashMap<>()); for (String precondition : ruleBean.getPreconditions()) { try { LOG.trace("Evaluating precondition: {}", precondition); if (!evaluateCondition(precondition, jexlContext)) { LOG.trace("Precondition {} failed, skipping rule {}", precondition, ruleBean.getUuid()); continue; } } catch (Throwable e) { LOG.error("Precondition {} failed, skipping rule {}: {}", precondition, ruleBean.getUuid(), e.toString(), e); continue; } } // All checks passed, so we will accept this rule builder.add(new RuntimeRule(ruleBean)); } this.rules = builder.build(); LOG.info("Loaded new Antenna Doctor engine with {} rules", this.rules.size()); } public List<AntennaDoctorMessage> onStage(AntennaDoctorStageContext context, Exception exception) { JexlContext jexlContext = new MapContext(); jexlContext.set("issue", new StageIssueJexl(exception)); jexlContext.set("stageDef", context.getStageDefinition()); jexlContext.set("stageConf", context.getStageConfiguration()); return evaluate(context, AntennaDoctorRuleBean.Entity.STAGE, jexlContext); } public List<AntennaDoctorMessage> onStage(AntennaDoctorStageContext context, ErrorCode errorCode, Object... args) { JexlContext jexlContext = new MapContext(); jexlContext.set("issue", new StageIssueJexl(errorCode, args)); jexlContext.set("stageDef", context.getStageDefinition()); jexlContext.set("stageConf", context.getStageConfiguration()); return evaluate(context, AntennaDoctorRuleBean.Entity.STAGE, jexlContext); } public List<AntennaDoctorMessage> onStage(AntennaDoctorStageContext context, String errorMessage) { JexlContext jexlContext = new MapContext(); jexlContext.set("issue", new StageIssueJexl(errorMessage)); jexlContext.set("stageDef", context.getStageDefinition()); jexlContext.set("stageConf", context.getStageConfiguration()); return evaluate(context, AntennaDoctorRuleBean.Entity.STAGE, jexlContext); } public List<AntennaDoctorMessage> onRest(AntennaDoctorContext context, ErrorCode errorCode, Object... args) { JexlContext jexlContext = new MapContext(); jexlContext.set("issue", new StageIssueJexl(errorCode, args)); return evaluate(context, AntennaDoctorRuleBean.Entity.REST, jexlContext); } public List<AntennaDoctorMessage> onRest(AntennaDoctorContext context, Exception exception) { JexlContext jexlContext = new MapContext(); jexlContext.set("issue", new StageIssueJexl(exception)); return evaluate(context, AntennaDoctorRuleBean.Entity.REST, jexlContext); } private List<AntennaDoctorMessage> evaluate(AntennaDoctorContext context, AntennaDoctorRuleBean.Entity entity, JexlContext jexlContext) { // All our expressions have the sdc object available jexlContext.set("sdc", new SdcJexl(context)); ImmutableList.Builder<AntennaDoctorMessage> builder = ImmutableList.builder(); // Iterate over rules and try to match them for (RuntimeRule rule : this.rules) { // Static check to execute only relevant rules if (rule.getEntity() != entity) { continue; } // Reset the variables that the rule is keeping jexlContext.set("context", new HashMap<>()); // Firstly evaluate conditions boolean matched = true; for (String condition : rule.getConditions()) { LOG.trace("Evaluating rule {} condition {}", rule.getUuid(), condition); try { if (!evaluateCondition(condition, jexlContext)) { matched = false; break; } } catch (JexlException e) { matched = false; LOG.error("Failed to evaluate rule {} condition {}: {}", rule.getUuid(), condition, e.toString(), e); break; } } // If all rules succeeded, evaluate message if (matched) { LOG.trace("Rule {} matched!", rule.getUuid()); try { StringWriter summaryWriter = new StringWriter(); StringWriter descriptionWriter = new StringWriter(); LOG.trace("Evaluating summary for rule {}: {}", rule.getUuid(), rule.getSummary()); templateEngine.createTemplate(rule.getSummary()).evaluate(jexlContext, summaryWriter); LOG.trace("Evaluating description for rule {}: {}", rule.getUuid(), rule.getDescription()); templateEngine.createTemplate(rule.getDescription()).evaluate(jexlContext, descriptionWriter); builder.add(new AntennaDoctorMessage(summaryWriter.toString(), descriptionWriter.toString())); } catch (JexlException e) { LOG.error("Failed to evaluate message for rule {}: {}", rule.getUuid(), e.toString(), e); } } else { LOG.trace("Rule {} did not match", rule.getUuid()); } } return builder.build(); } private boolean evaluateCondition(String condition, JexlContext context) { Object output = engine.createExpression(condition).evaluate(context); if (output != null && Boolean.class.isAssignableFrom(output.getClass())) { return (boolean) output; } // We consider any non-boolean value as true (continue) return true; } }