package rocks.inspectit.server.diagnosis.engine.rule;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import rocks.inspectit.server.diagnosis.engine.rule.exception.RuleExecutionException;
import rocks.inspectit.server.diagnosis.engine.tag.Tag;
import rocks.inspectit.server.diagnosis.engine.util.ReflectionUtils;
/**
* A {@link RuleDefinition} is an abstracted and generalized view of a rule implementation. Each
* rule implementation which is passed to the
* {@link rocks.inspectit.server.diagnosis.engine.DiagnosisEngine} is converted in a
* <code>RuleDefinition</code>.
* <p>
* <p>
*
* <pre>
* A <code>RuleDefinition</code> summarizes
* <ul>
* <li>The name of a rule</li>
* <li>The description of a rule</li>
* <li>The implementing class of a rule</li>
* <li>The {@link FireCondition} of a rule</li>
* <li>The {@link TagInjection}s of a rule</li>
* <li>The {@link SessionVariableInjection}s of a rule</li>
* <li>The {@link ConditionMethod}s of a rule</li>
* </ul>
* </pre>
*
* @author Claudio Waldvogel, Alexander Wert
* @see FireCondition
* @see TagInjection
* @see ConditionMethod
*/
public class RuleDefinition {
/**
* The default description of a rule.
*/
public static final String EMPTY_DESCRIPTION = "EMPTY";
/**
* The name of this rule.
*/
private String name;
/**
* The description of this rule.
*/
private String description;
/**
* The backing implementation class of this rule.
*/
private Class<?> implementation;
/**
* The {@link FireCondition} of this rule.
*/
private FireCondition fireCondition;
/**
* The required {@link TagInjection}s of this rule.
*
* @see TagInjection
*/
private List<TagInjection> tagInjections;
/**
* The required {@link SessionVariableInjection}s of this rule.
*
* @see SessionVariableInjection
*/
private List<SessionVariableInjection> variableInjections;
/**
* The {@link ConditionMethod}s of this rule.
*
* @see ConditionMethod
*/
private List<ConditionMethod> conditionMethods;
/**
* The {@link ActionMethod}s of this rule.
*
* @see ActionMethod
*/
private ActionMethod actionMethod;
/**
* Private constructor.
*/
protected RuleDefinition() {
}
// -------------------------------------------------------------
// Methods: RuleExecution
// -------------------------------------------------------------
/**
* Executes this {@link RuleDefinition} in 6 steps.
*
* <pre>
* 1. The raw class which implements this <code>RuleDefinition</code> is instantiated and wrapped in a new {@link ExecutionContext}.
* 2. All {@link TagInjection}s are executed.
* 3. All {@link SessionVariableInjection}s are executed
* 4. All {@link ConditionMethod}s are executed.
* 5. If all {@link ConditionMethod}s succeed, the {@link ActionMethod} is executed.
* 6. A new {@link RuleOutput} is created and returned
* </pre>
*
* @param input
* The {@link RuleInput} to be processed. Must not null.
* @param variables
* The session variables. Must not be null.
* @return A new {@link RuleOutput}
* @throws RuleExecutionException
* If rule execution fails.
* @see ExecutionContext
* @see RuleInput
* @see RuleOutput
*/
public RuleOutput execute(RuleInput input, Map<String, Object> variables) throws RuleExecutionException {
checkNotNull(input, "The RuleInput must not be null!");
checkNotNull(variables, "The Session Variables must not be null!");
// Create a new ExecutionContext for this run
ExecutionContext ctx = new ExecutionContext(this, ReflectionUtils.tryInstantiate(getImplementation()), input, variables);
// Inject tags
for (TagInjection injection : getTagInjections()) {
injection.execute(ctx);
}
// Inject session variables
for (SessionVariableInjection injection : getSessionVariableInjections()) {
injection.execute(ctx);
}
// Check conditions
Collection<ConditionFailure> conditionFailures = null;
for (ConditionMethod conditionMethod : getConditionMethods()) {
ConditionFailure failure = conditionMethod.execute(ctx);
if (failure != null) {
if (null == conditionFailures) {
conditionFailures = Lists.newArrayList();
}
conditionFailures.add(failure);
}
}
// If no condition failed, execute the actual action
Collection<Tag> tags = null;
if (CollectionUtils.isEmpty(conditionFailures)) {
conditionFailures = Collections.emptyList();
tags = getActionMethod().execute(ctx);
} else {
tags = Collections.emptyList();
}
// Deliver result
return new RuleOutput(getName(), getActionMethod().getResultTag(), conditionFailures, tags);
}
/**
* Convenience method to execute this <code>RuleDefinition</code> for several {@link RuleInput}
* s. The amount of {@link RuleInput}s equals the amount of executions of this
* <code>RuleDefinition</code>. Each {@link RuleInput} concludes in a invocation of
* {@link #execute(RuleInput, Map<String, Object>)}.
*
* @param inputs
* A collection of {@link RuleInput} to be processed.
* @param variables
* The session variables.
* @return A collection of {@link RuleOutput}s.
* @throws RuleExecutionException
* If rule execution fails.
* @see RuleInput
* @see RuleOutput
*/
public Collection<RuleOutput> execute(Collection<RuleInput> inputs, Map<String, Object> variables) throws RuleExecutionException {
checkNotNull(inputs, "The RuleInputs must not be null!");
Iterator<RuleInput> iterator = inputs.iterator();
Set<RuleOutput> outputs = Sets.newHashSet();
while (iterator.hasNext()) {
outputs.add(execute(iterator.next(), variables));
}
return outputs;
}
// -------------------------------------------------------------
// Methods: Accessors
// -------------------------------------------------------------
/**
* Gets {@link #name}.
*
* @return {@link #name}
*/
public String getName() {
return name;
}
/**
* Gets {@link #description}.
*
* @return {@link #description}
*/
public String getDescription() {
return description;
}
/**
* Gets {@link #implementation}.
*
* @return {@link #implementation}
*/
public Class<?> getImplementation() {
return implementation;
}
/**
* Gets {@link #fireCondition}.
*
* @return {@link #fireCondition}
*/
public FireCondition getFireCondition() {
return fireCondition;
}
/**
* Gets {@link #tagInjections}.
*
* @return {@link #tagInjections}
*/
public List<TagInjection> getTagInjections() {
return tagInjections;
}
/**
* Gets {@link #variableInjections}.
*
* @return {@link #variableInjections}
*/
public List<SessionVariableInjection> getSessionVariableInjections() {
return variableInjections;
}
/**
* Gets {@link #actionMethod}.
*
* @return {@link #actionMethod}
*/
public ActionMethod getActionMethod() {
return actionMethod;
}
/**
* Gets {@link #conditionMethods}.
*
* @return {@link #conditionMethods}
*/
public List<ConditionMethod> getConditionMethods() {
return conditionMethods;
}
/**
* Sets {@link #variableInjections}.
*
* @param variableInjections
* New value for {@link #variableInjections}
*/
protected void setSessionVariableInjections(List<SessionVariableInjection> variableInjections) {
this.variableInjections = variableInjections;
}
/**
* Sets {@link #name}.
*
* @param name
* New value for {@link #name}
*/
protected void setName(String name) {
this.name = name;
}
/**
* Sets {@link #description}.
*
* @param description
* New value for {@link #description}
*/
protected void setDescription(String description) {
this.description = description;
}
/**
* Sets {@link #implementation}.
*
* @param implementation
* New value for {@link #implementation}
*/
protected void setImplementation(Class<?> implementation) {
this.implementation = implementation;
}
/**
* Sets {@link #fireCondition}.
*
* @param fireCondition
* New value for {@link #fireCondition}
*/
protected void setFireCondition(FireCondition fireCondition) {
this.fireCondition = fireCondition;
}
/**
* Sets {@link #tagInjections}.
*
* @param tagInjections
* New value for {@link #tagInjections}
*/
protected void setTagInjections(List<TagInjection> tagInjections) {
this.tagInjections = tagInjections;
}
/**
* Sets {@link #conditionMethods}.
*
* @param conditionMethods
* New value for {@link #conditionMethods}
*/
protected void setConditionMethods(List<ConditionMethod> conditionMethods) {
this.conditionMethods = conditionMethods;
}
/**
* Sets {@link #actionMethod}.
*
* @param actionMethod
* New value for {@link #actionMethod}
*/
protected void setActionMethod(ActionMethod actionMethod) {
this.actionMethod = actionMethod;
}
// -------------------------------------------------------------
// Methods: Generated
// -------------------------------------------------------------
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "RuleDefinition{" + "name='" + name + '\'' + ", description='" + description + '\'' + ", implementation=" + implementation + ", fireCondition=" + fireCondition + ", injectionPoints="
+ tagInjections + ", actionMethod=" + actionMethod + ", conditionMethods=" + conditionMethods + '}';
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((this.actionMethod == null) ? 0 : this.actionMethod.hashCode());
result = (prime * result) + ((this.conditionMethods == null) ? 0 : this.conditionMethods.hashCode());
result = (prime * result) + ((this.description == null) ? 0 : this.description.hashCode());
result = (prime * result) + ((this.fireCondition == null) ? 0 : this.fireCondition.hashCode());
result = (prime * result) + ((this.implementation == null) ? 0 : this.implementation.hashCode());
result = (prime * result) + ((this.name == null) ? 0 : this.name.hashCode());
result = (prime * result) + ((this.tagInjections == null) ? 0 : this.tagInjections.hashCode());
result = (prime * result) + ((this.variableInjections == null) ? 0 : this.variableInjections.hashCode());
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
RuleDefinition other = (RuleDefinition) obj;
if (this.actionMethod == null) {
if (other.actionMethod != null) {
return false;
}
} else if (!this.actionMethod.equals(other.actionMethod)) {
return false;
}
if (this.conditionMethods == null) {
if (other.conditionMethods != null) {
return false;
}
} else if (!this.conditionMethods.equals(other.conditionMethods)) {
return false;
}
if (this.description == null) {
if (other.description != null) {
return false;
}
} else if (!this.description.equals(other.description)) {
return false;
}
if (this.fireCondition == null) {
if (other.fireCondition != null) {
return false;
}
} else if (!this.fireCondition.equals(other.fireCondition)) {
return false;
}
if (this.implementation == null) {
if (other.implementation != null) {
return false;
}
} else if (!this.implementation.equals(other.implementation)) {
return false;
}
if (this.name == null) {
if (other.name != null) {
return false;
}
} else if (!this.name.equals(other.name)) {
return false;
}
if (this.tagInjections == null) {
if (other.tagInjections != null) {
return false;
}
} else if (!this.tagInjections.equals(other.tagInjections)) {
return false;
}
if (this.variableInjections == null) {
if (other.variableInjections != null) {
return false;
}
} else if (!this.variableInjections.equals(other.variableInjections)) {
return false;
}
return true;
}
/**
* Builder for {@link RuleDefinition} instances to ensure immutability of the
* {@link RuleDefinition}.
*
* @author Alexander Wert
*
*/
public static class RuleDefinitionBuilder {
/**
* {@link RuleDefinition} instance under construction.
*/
private final RuleDefinition objectUnderConstruction;
/**
* Constructor.
*/
public RuleDefinitionBuilder() {
objectUnderConstruction = new RuleDefinition();
}
/**
* Sets {@link RuleDefinition#variableInjections}.
*
* @param variableInjections
* New value for {@link RuleDefinition#variableInjections}
*/
public void setSessionVariableInjections(List<SessionVariableInjection> variableInjections) {
objectUnderConstruction.setSessionVariableInjections(variableInjections);
}
/**
* Sets {@link RuleDefinition#name}.
*
* @param name
* New value for {@link RuleDefinition#name}
*/
public void setName(String name) {
objectUnderConstruction.setName(name);
}
/**
* Sets {@link RuleDefinition#description}.
*
* @param description
* New value for {@link RuleDefinition#description}
*/
public void setDescription(String description) {
objectUnderConstruction.setDescription(description);
}
/**
* Sets {@link RuleDefinition#implementation}.
*
* @param implementation
* New value for {@link RuleDefinition#implementation}
*/
public void setImplementation(Class<?> implementation) {
objectUnderConstruction.setImplementation(implementation);
}
/**
* Sets {@link RuleDefinition#fireCondition}.
*
* @param fireCondition
* New value for {@link RuleDefinition#fireCondition}
*/
public void setFireCondition(FireCondition fireCondition) {
objectUnderConstruction.setFireCondition(fireCondition);
}
/**
* Sets {@link RuleDefinition#tagInjections}.
*
* @param tagInjections
* New value for {@link RuleDefinition#tagInjections}
*/
public void setTagInjections(List<TagInjection> tagInjections) {
objectUnderConstruction.setTagInjections(tagInjections);
}
/**
* Sets {@link RuleDefinition#conditionMethods}.
*
* @param conditionMethods
* New value for {@link RuleDefinition#conditionMethods}
*/
public void setConditionMethods(List<ConditionMethod> conditionMethods) {
objectUnderConstruction.setConditionMethods(conditionMethods);
}
/**
* Sets {@link RuleDefinition#actionMethod}.
*
* @param actionMethod
* New value for {@link RuleDefinition#actionMethod}
*/
public void setActionMethod(ActionMethod actionMethod) {
objectUnderConstruction.setActionMethod(actionMethod);
}
/**
* Creates a {@link RuleDefinition} instance according to the set attributes. Before calling
* this method, the following properties must be set:
* <ul>
* <li>{@link RuleDefinitionBuilder#setSessionVariableInjections(List)}</li>
* <li>{@link RuleDefinitionBuilder#setImplementation}</li>
* <li>{@link RuleDefinitionBuilder#setFireCondition}</li>
* <li>{@link RuleDefinitionBuilder#setTagInjections}</li>
* <li>{@link RuleDefinitionBuilder#setConditionMethods}</li>
* <li>{@link RuleDefinitionBuilder#setActionMethod}</li>
* </ul>
*
* @return Returns a {@link RuleDefinition} instance.
*/
public RuleDefinition build() {
if (null == objectUnderConstruction.getSessionVariableInjections()) {
throw new IllegalStateException("Cannot create a RuleDefinition. Missing Session Variable Injections!");
}
if (null == objectUnderConstruction.getImplementation()) {
throw new IllegalStateException("Cannot create a RuleDefinition. Missing Rule Implementation!");
}
if (null == objectUnderConstruction.getFireCondition()) {
throw new IllegalStateException("Cannot create a RuleDefinition. Missing Fire Condition!");
}
if (CollectionUtils.isEmpty(objectUnderConstruction.getTagInjections())) {
throw new IllegalStateException("Cannot create a RuleDefinition. Missing Tag Injections!");
}
if (null == objectUnderConstruction.getConditionMethods()) {
throw new IllegalStateException("Cannot create a RuleDefinition. Missing Condition Methods!");
}
if (null == objectUnderConstruction.getActionMethod()) {
throw new IllegalStateException("Cannot create a RuleDefinition. Missing Action Method!");
}
objectUnderConstruction.setName(StringUtils.defaultIfEmpty(objectUnderConstruction.getName(), objectUnderConstruction.getImplementation().getName()));
objectUnderConstruction.setDescription(StringUtils.defaultIfEmpty(objectUnderConstruction.getDescription(), EMPTY_DESCRIPTION));
return objectUnderConstruction;
}
}
}