com.memority.toolkit.rule.api.RuleDefinition Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of toolkit-rule-api Show documentation
Show all versions of toolkit-rule-api Show documentation
This artifact provides the API classes that are necessary to implement the contracts of Memority configuration Rules.
/*
* Copyright (c) 2016-2023 Memority. All Rights Reserved.
*
* This file is part of Memority Toolkit API , a Memority project.
*
* This file is released under the Memority Public Artifacts End-User License Agreement,
* see
* Unauthorized copying of this file, via any medium is strictly prohibited.
*/
package com.memority.toolkit.rule.api;
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.eclipse.persistence.oxm.annotations.XmlCDATA;
import org.eclipse.persistence.oxm.annotations.XmlReadOnly;
import org.hibernate.validator.constraints.Length;
import javax.validation.Valid;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.io.Serializable;
import java.util.regex.Pattern;
/**
* RuleDefinition is a serializable object used to describe a Rule.
* It contains:
*
* - A description of the rule, either:
*
* - a
script
in Groovy
* - a
classLabel (class)
describing the builtin Java class to use
* - or a
delegateRef (ref)
pointing to the externally configured Rule to use
*
*
* - an optional
configuration (config)
for java rules
*
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement(name = XmlConstants.NAME_ELEMENT_RULEDEFINITION)
@XmlType(name = XmlConstants.NAME_TYPE_RULEDEFINITION)
@XmlAccessorType(XmlAccessType.PROPERTY)
@EqualsAndHashCode
public class RuleDefinition implements Serializable {
private static final Pattern RULE_SPEC_JAVARULE_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+[a-zA-Z0-9\\-_]*[a-zA-Z0-9]+:.*$");
@Length(max = 1_048_576) //to be coherent with HBM
private String script;
private String delegateRef;
private String classLabel;
@Valid
private RuleConfiguration configuration;
private LegacyParams legacyParams;
@SuppressWarnings({"unused", "squid:S2637"}) // for serialization
protected RuleDefinition() {
}
public RuleDefinition(String script,
String classLabel,
String delegateRef,
RuleConfiguration configuration) {
this(script, classLabel, delegateRef, configuration, null, null, null);
}
@JsonCreator
public RuleDefinition(
// new, simplified configuration serialization
@JsonProperty(value = "script") String script,
@JsonProperty(value = "class") String classLabel,
@JsonProperty(value = "ref") String delegateRef,
@JsonProperty(value = "config") RuleConfiguration config,
// old school configuration serialization (some stuff we do not even care about
// anymore, such as type and category)
@JsonProperty(value = "engineType") RuleEngineType engineType,
@JsonProperty(value = "ruleSpec") String ruleSpec,
@JsonProperty(value = "configuration") RuleConfiguration configuration) {
if (ruleSpec != null) {
// Old style
this.legacyParams = new LegacyParams(engineType, ruleSpec, configuration);
this.setupFromLegacyParams();
} else {
this.setDelegateRef(delegateRef);
this.setClassLabel(classLabel);
this.setScript(script);
this.configuration = config;
}
}
private void setupFromLegacyParams() {
if (this.legacyParams == null) {
return;
}
RuleEngineType ruleEngineType = this.legacyParams.ruleEngineType;
String ruleSpec = this.legacyParams.ruleSpec;
String content = ruleSpec;
if (ruleEngineType == RuleEngineType.JAVA
&& ruleSpec != null
&& RULE_SPEC_JAVARULE_PATTERN.matcher(ruleSpec.trim()).matches()) {
// Extract label as first part of rule spec
content = ruleSpec.substring(0, ruleSpec.indexOf(':'));
}
switch (this.legacyParams.ruleEngineType) {
case GROOVY:
setScript(content);
break;
case JAVA:
setClassLabel(content);
// In case of JSON, the ruleConfiguration in legacy will be set (handled as the old 'configuration'
// json property)
// In case of XML however, the property was already 'config' and it's not mapped to legacy, so we
// are careful to propagate the legacy configuration _only_ if there is one
if (this.legacyParams.ruleConfiguration != null) {
setConfig(this.legacyParams.ruleConfiguration);
}
break;
case DELEGATE:
setDelegateRef(content);
break;
}
// Erase legacy params into oblivion
this.legacyParams = null;
}
/**
* Get the configured engine type for this rule definition.
*
* @throws IllegalStateException if the engine type cannot be infered (this error is ignored when resolving relations and is catched by validation).
* @return The rule engine type.
*/
@XmlTransient
@JsonIgnore
public RuleEngineType getEngineType() {
if (StringUtils.isNotBlank(this.script)) {
return RuleEngineType.GROOVY;
} else if (StringUtils.isNotBlank(this.classLabel)) {
return RuleEngineType.JAVA;
} else if (StringUtils.isNotBlank(this.delegateRef)) {
return RuleEngineType.DELEGATE;
} else {
throw new IllegalStateException("RuleDefinition should have either a script, class label or delegate ref. Cannot infer engine type!");
}
}
@XmlAttribute(name = "engineType")
@XmlReadOnly
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Deprecated
public RuleEngineType getLegacyRuleEngineType() { return this.legacyParams == null ? null : this.legacyParams.ruleEngineType; }
@Deprecated
public void setLegacyRuleEngineType(RuleEngineType ruleEngineType) {
if (this.legacyParams == null) {
this.legacyParams = new LegacyParams();
}
this.legacyParams.ruleEngineType = ruleEngineType;
}
@XmlElement(name = "spec")
@XmlReadOnly
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Deprecated
public String getLegacyRuleSpec() {
return this.legacyParams == null ? null : this.legacyParams.ruleSpec;
}
/**
* This method is here solely with the purpose of supporting old style deserialization
*
* @param ruleSpec the serialized representation
*/
@Deprecated
public void setLegacyRuleSpec(String ruleSpec) {
if (this.legacyParams == null) {
this.legacyParams = new LegacyParams();
}
this.legacyParams.ruleSpec = ruleSpec;
}
@XmlTransient
@JsonProperty(value = "configuration", access = JsonProperty.Access.READ_ONLY)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
@Deprecated
public RuleConfiguration getLegacyRuleConfiguration() {
return this.legacyParams == null ? null : this.legacyParams.ruleConfiguration;
}
/**
* This method is here solely with the purpose of supporting old style deserialization
*
* @param ruleConfiguration the rule configuration
*/
@Deprecated
public void setLegacyRuleConfiguration(RuleConfiguration ruleConfiguration) {
if (this.legacyParams == null) {
this.legacyParams = new LegacyParams();
}
this.legacyParams.ruleConfiguration = ruleConfiguration;
}
/**
* This method is there to infer correct content in case of legacy XML deserialization,
* where the engine inferring properties (script, class, ref) have not been set.
*/
void afterUnmarshal(Unmarshaller u, Object parent) {
setupFromLegacyParams();
}
@XmlElement(name = "config")
@JsonProperty("config")
@JsonTypeInfo(
use = JsonTypeInfo.Id.CUSTOM,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
property = "class",
defaultImpl = RuleConfiguration.EmptyRuleConfiguration.class // required to handle missing 'class' but present 'config'
)
@JsonTypeIdResolver(RuleConfiguration.RuleConfigurationTypeIdResolver.class)
public RuleConfiguration getConfig() {
return configuration;
}
public void setConfig(RuleConfiguration configuration) {
this.configuration = configuration;
}
@XmlAttribute(name = "class")
@JsonProperty("class")
public String getClassLabel() {
return classLabel;
}
public void setClassLabel(String classLabel) {
// Workaround issue in Jackson deserialization when there is a 'config' property but class is actually null.
this.classLabel = "null".equals(classLabel) ? null : classLabel;
}
@XmlAttribute(name = "ref")
@JsonProperty("ref")
public String getDelegateRef() {
return delegateRef;
}
public void setDelegateRef(String delegateRef) {
this.delegateRef = delegateRef;
}
@XmlCDATA
@JsonProperty("script")
public String getScript() {
return this.script;
}
public void setScript(String script) {
this.script = script;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
@Data
@NoArgsConstructor
@AllArgsConstructor
private static class LegacyParams {
RuleEngineType ruleEngineType;
String ruleSpec;
RuleConfiguration ruleConfiguration;
}
}