From b69c38c7f0aac87cb5788e4d9a38660fb20137ad Mon Sep 17 00:00:00 2001 From: Jonathan Gamba Date: Wed, 29 Jan 2020 08:18:37 -0600 Subject: [PATCH] Adding Jackson code in order to allow importing of rules --- .../rules/RuleComponentDefinition.java | 58 ++++++++- .../rules/conditionlet/Conditionlet.java | 40 +++++- .../rules/parameter/ParameterDefinition.java | 70 ++++++++++- .../rules/parameter/display/Input.java | 48 +++++++- .../rules/parameter/type/DataType.java | 68 +++++++++- .../rules/util/RulesImportExportUtil.java | 116 +++++++++--------- 6 files changed, 333 insertions(+), 67 deletions(-) diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/RuleComponentDefinition.java b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/RuleComponentDefinition.java index 297e9da3b533..80b2c4f77717 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/RuleComponentDefinition.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/RuleComponentDefinition.java @@ -10,10 +10,15 @@ import com.dotmarketing.portlets.rules.model.ParameterModel; import com.dotmarketing.portlets.rules.parameter.ParameterDefinition; import com.dotmarketing.util.Logger; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.io.Serializable; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.elasticsearch.common.Nullable; /** * @author Geoff M. Granum @@ -26,7 +31,20 @@ public abstract class RuleComponentDefinition i private final Map parameterDefinitions; protected RuleComponentDefinition(String i18nKey, ParameterDefinition... parameterDefinitions) { - this.id = this.getClass().getSimpleName(); + this(null, i18nKey, parameterDefinitions); + } + + /** + * @deprecated Do not use this method directly, it exists only for the Jackson-binding + * infrastructure + */ + protected RuleComponentDefinition(String id, String i18nKey, + ParameterDefinition... parameterDefinitions) { + + if (null == id) { + id = this.getClass().getSimpleName(); + } + this.id = id; this.i18nKey = i18nKey; Map defs = Maps.newLinkedHashMap(); for (ParameterDefinition def : parameterDefinitions) { @@ -120,5 +138,41 @@ public String toLogString(){ } public abstract boolean evaluate(HttpServletRequest request, HttpServletResponse response, T instance); -} + /** + * Utility type used to correctly read immutable object from JSON representation. + * + * @deprecated Do not use this type directly, it exists only for the Jackson-binding + * infrastructure + */ + @Deprecated + @JsonDeserialize + @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE) + static protected final class Json { + + @javax.annotation.Nullable + public String id; + @javax.annotation.Nullable + public String i18nKey; + @javax.annotation.Nullable + public Map parameterDefinitions; + + @JsonProperty("id") + public void setId(@Nullable String id) { + this.id = id; + } + + @JsonProperty("i18nKey") + public void setI18nKey(@Nullable String i18nKey) { + this.i18nKey = i18nKey; + } + + @JsonProperty("parameterDefinitions") + public void setParameterDefinitions( + @Nullable Map parameterDefinitions) { + this.parameterDefinitions = parameterDefinitions; + } + } + +} \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/conditionlet/Conditionlet.java b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/conditionlet/Conditionlet.java index 7ae27f4462fa..73373330a3d8 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/conditionlet/Conditionlet.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/conditionlet/Conditionlet.java @@ -3,12 +3,50 @@ import com.dotmarketing.portlets.rules.RuleComponentDefinition; import com.dotmarketing.portlets.rules.RuleComponentInstance; import com.dotmarketing.portlets.rules.parameter.ParameterDefinition; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; public abstract class Conditionlet extends RuleComponentDefinition { public static final String COMPARISON_KEY = "comparison"; + /** + * @deprecated Do not use this method directly, it exists only for the Jackson-binding + * infrastructure + */ + protected Conditionlet(String id, String i18nKey, ParameterDefinition... parameterDefinitions) { + super(id, i18nKey, parameterDefinitions); + } + protected Conditionlet(String i18nKey, ParameterDefinition... parameterDefinitions) { super(i18nKey, parameterDefinitions); } -} + + /** + * @param json A JSON-bindable data structure + * @deprecated Do not use this method directly, it exists only for the Jackson-binding + * infrastructure + */ + @Deprecated + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + static Conditionlet fromJson(RuleComponentDefinition.Json json) { + + return new Conditionlet(json.id, json.i18nKey, + json.parameterDefinitions.values().toArray(new ParameterDefinition[]{})) { + + @Override + public RuleComponentInstance instanceFrom(Map parameters) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean evaluate(HttpServletRequest request, HttpServletResponse response, + RuleComponentInstance instance) { + throw new UnsupportedOperationException(); + } + }; + } + +} \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/ParameterDefinition.java b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/ParameterDefinition.java index 13a54ed66734..633da7c31c1f 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/ParameterDefinition.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/ParameterDefinition.java @@ -6,7 +6,13 @@ import com.dotmarketing.portlets.rules.model.ParameterModel; import com.dotmarketing.portlets.rules.parameter.display.Input; import com.dotmarketing.portlets.rules.parameter.type.DataType; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.apache.commons.lang.StringUtils; +import org.elasticsearch.common.Nullable; public class ParameterDefinition { @@ -58,5 +64,67 @@ public Input getInputType() { public void checkValid(ParameterModel model) throws InvalidRuleParameterException, RuleEngineException { this.inputType.checkValid(model.getValue()); } -} + /** + * Utility type used to correctly read immutable object from JSON representation. + * + * @deprecated Do not use this type directly, it exists only for the Jackson-binding + * infrastructure + */ + @Deprecated + @JsonDeserialize + @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE) + static final class Json { + + @javax.annotation.Nullable + String key; + @javax.annotation.Nullable + String i18nBaseKey; + @javax.annotation.Nullable + String defaultValue; + @javax.annotation.Nullable + Input inputType; + @javax.annotation.Nullable + int priority; + + @JsonProperty("key") + public void setKey(@Nullable String key) { + this.key = key; + } + + @JsonProperty("i18nBaseKey") + public void setI18nBaseKey(@Nullable String i18nBaseKey) { + this.i18nBaseKey = i18nBaseKey; + } + + @JsonProperty("defaultValue") + public void setDefaultValue(@Nullable String defaultValue) { + this.defaultValue = defaultValue; + } + + @JsonProperty("inputType") + public void setInputType(@Nullable Input inputType) { + this.inputType = inputType; + } + + @JsonProperty("priority") + public void setPriority(@Nullable int priority) { + this.priority = priority; + } + + } + + /** + * @param json A JSON-bindable data structure + * @deprecated Do not use this method directly, it exists only for the Jackson-binding + * infrastructure + */ + @Deprecated + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + static ParameterDefinition fromJson(ParameterDefinition.Json json) { + return new ParameterDefinition(json.priority, json.key, json.i18nBaseKey, json.inputType, + json.defaultValue); + } + +} \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/display/Input.java b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/display/Input.java index 0e10e029c058..f8ae99eed6b7 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/display/Input.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/display/Input.java @@ -2,6 +2,12 @@ import com.dotcms.rest.exception.InvalidRuleParameterException; import com.dotmarketing.portlets.rules.parameter.type.DataType; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.elasticsearch.common.Nullable; /** * @author Geoff M. Granum @@ -32,5 +38,45 @@ public T getDataType() { public void checkValid(String value) throws InvalidRuleParameterException{ return; } -} + /** + * Utility type used to correctly read immutable object from JSON representation. + * + * @deprecated Do not use this type directly, it exists only for the Jackson-binding + * infrastructure + */ + @Deprecated + @JsonDeserialize + @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE) + static final class Json { + + @javax.annotation.Nullable + String id; + @javax.annotation.Nullable + DataType dataType; + + @JsonProperty("id") + public void setKey(@Nullable String id) { + this.id = id; + } + + @JsonProperty("dataType") + public void setDataType(@Nullable DataType dataType) { + this.dataType = dataType; + } + + } + + /** + * @param json A JSON-bindable data structure + * @deprecated Do not use this method directly, it exists only for the Jackson-binding + * infrastructure + */ + @Deprecated + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + static Input fromJson(Input.Json json) { + return new Input(json.id, json.dataType); + } + +} \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/type/DataType.java b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/type/DataType.java index 41a6056ad011..8f058134453a 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/type/DataType.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/parameter/type/DataType.java @@ -3,9 +3,15 @@ import com.dotcms.repackage.com.google.common.collect.ImmutableMap; import com.dotcms.repackage.com.google.common.collect.Maps; import com.dotmarketing.portlets.rules.parameter.type.constraint.TypeConstraint; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.util.Collections; import java.util.Map; import org.apache.commons.lang.StringUtils; +import org.elasticsearch.common.Nullable; /** * @author Geoff M. Granum @@ -23,6 +29,12 @@ public DataType(String id, String errorMessageKey) { this.errorMessageKey = errorMessageKey; } + public DataType(String id, String errorMessageKey, Map restrictions) { + this.id = id; + this.errorMessageKey = errorMessageKey; + this.restrictions = restrictions; + } + public String getId() { return id; } @@ -77,5 +89,57 @@ public DataType restrict(TypeConstraint restriction) { public Map getConstraints() { return ImmutableMap.copyOf(this.restrictions); } -} - + + /** + * Utility type used to correctly read immutable object from JSON representation. + * + * @deprecated Do not use this type directly, it exists only for the Jackson-binding + * infrastructure + */ + @Deprecated + @JsonDeserialize + @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE) + static final class Json { + + @javax.annotation.Nullable + String id; + @javax.annotation.Nullable + String errorMessageKey; + @javax.annotation.Nullable + Map restrictions; + + @JsonProperty("id") + public void setKey(@Nullable String id) { + this.id = id; + } + + @JsonProperty("errorMessageKey") + public void setDataType(@Nullable String errorMessageKey) { + this.errorMessageKey = errorMessageKey; + } + + @JsonProperty("constraints") + public void setRestrictions(@Nullable Map restrictions) { + this.restrictions = restrictions; + } + + } + + /** + * @param json A JSON-bindable data structure + * @deprecated Do not use this method directly, it exists only for the Jackson-binding + * infrastructure + */ + @Deprecated + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + static DataType fromJson(DataType.Json json) { + return new DataType(json.id, json.errorMessageKey, json.restrictions) { + @Override + public Object convert(String from) { + throw new UnsupportedOperationException(); + } + }; + } + +} \ No newline at end of file diff --git a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/util/RulesImportExportUtil.java b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/util/RulesImportExportUtil.java index 655038143590..63229289f80c 100644 --- a/dotCMS/src/main/java/com/dotmarketing/portlets/rules/util/RulesImportExportUtil.java +++ b/dotCMS/src/main/java/com/dotmarketing/portlets/rules/util/RulesImportExportUtil.java @@ -1,18 +1,9 @@ package com.dotmarketing.portlets.rules.util; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.util.List; - - +import com.dotcms.business.CloseDBIfOpened; +import com.dotcms.business.WrapInTransaction; import com.dotcms.enterprise.rules.RulesAPI; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.dotcms.util.CloseUtils; import com.dotmarketing.business.APILocator; import com.dotmarketing.business.Ruleable; import com.dotmarketing.exception.DotDataException; @@ -22,7 +13,18 @@ import com.dotmarketing.portlets.rules.model.Rule; import com.dotmarketing.portlets.rules.model.RuleAction; import com.dotmarketing.util.Logger; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; import com.liferay.portal.model.User; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.StringWriter; +import java.util.List; /** * @@ -35,10 +37,6 @@ public class RulesImportExportUtil { private static RulesImportExportUtil rulesImportExportUtil; - /** - * - * @return - */ public static RulesImportExportUtil getInstance() { if (rulesImportExportUtil == null) { synchronized ("RulesImportExportUtil.class") { @@ -50,23 +48,11 @@ public static RulesImportExportUtil getInstance() { return rulesImportExportUtil; } - /** - * - * @return - * @throws IOException - * @throws DotDataException - * @throws DotSecurityException - */ public String exportToJson() throws IOException, DotDataException, DotSecurityException { ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(buildExportObject(null)); } - /** - * - * @param file - * @throws IOException - */ public void export(File file) throws IOException { BufferedWriter out = null; @@ -83,69 +69,79 @@ public void export(File file) throws IOException { } } - /** - * - * @param file - * @throws IOException - */ public void importRules(File file) throws IOException { - RulesAPI rapi = APILocator.getRulesAPI(); + this.importRules(new FileReader(file)); + } + + public void importRules(final Reader reader) throws IOException { + + final ObjectMapper mapper = new ObjectMapper(); + final StringWriter stringWriter = new StringWriter(); + BufferedReader bufferedReader = null; + RulesImportExportObject importer = null; - BufferedReader in = null; try { - User user = APILocator.getUserAPI().getSystemUser(); - FileReader fstream = new FileReader(file); - in = new BufferedReader(fstream); - ObjectMapper mapper = new ObjectMapper(); + + bufferedReader = new BufferedReader(reader); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - StringWriter sw = new StringWriter(); + String str; - while ((str = in.readLine()) != null) { - sw.append(str); + while ((str = bufferedReader.readLine()) != null) { + stringWriter.append(str); } - RulesImportExportObject importer = mapper.readValue(sw.toString(), RulesImportExportObject.class); + importer = mapper.readValue + ((String) stringWriter.toString(), RulesImportExportObject.class); + + this.importRules(importer, APILocator.systemUser()); + } catch (Exception e) { + Logger.error(this.getClass(), "Error: " + e.getMessage(), e); + } finally { + CloseUtils.closeQuietly(bufferedReader); + } + } + + @WrapInTransaction + public void importRules(final RulesImportExportObject importer, + final User user) throws IOException, DotDataException { + + try { + final RulesAPI rulesAPI = APILocator.getRulesAPI(); //Saving the rules. for (Rule rule : importer.getRules()) { rule.setParentPermissionable(RulePermissionableUtil.findParentPermissionable(rule.getParent())); - rapi.saveRule(rule, user, false); + rulesAPI.saveRule(rule, user, false); //Saving the Condition Groups. for (ConditionGroup group : rule.getGroups()) { - rapi.saveConditionGroup(group, user, false); + rulesAPI.saveConditionGroup(group, user, false); //Saving the Condition for each group. for (Condition condition : group.getConditions()) { - rapi.saveCondition(condition, user, false); + rulesAPI.saveCondition(condition, user, false); } } //Saving the Action. for (RuleAction action : rule.getRuleActions()) { - rapi.saveRuleAction(action, user, false); + rulesAPI.saveRuleAction(action, user, false); } } - } catch (Exception e) { Logger.error(this.getClass(), "Error: " + e.getMessage(), e); - } finally { - in.close(); + throw new DotDataException(e); } } - /** - * - * @param parent - * @return - * @throws DotDataException - * @throws DotSecurityException - */ - public RulesImportExportObject buildExportObject(Ruleable parent) throws DotDataException, DotSecurityException { + @CloseDBIfOpened + public RulesImportExportObject buildExportObject(Ruleable parent) + throws DotDataException, DotSecurityException { - RulesAPI rapi = APILocator.getRulesAPI(); + RulesAPI rulesAPI = APILocator.getRulesAPI(); User user = APILocator.getUserAPI().getSystemUser(); - List rules = (parent == null) ? rapi.getAllRules(user, false) : rapi.getAllRulesByParent(parent, user, false); + List rules = (parent == null) ? rulesAPI.getAllRules(user, false) + : rulesAPI.getAllRulesByParent(parent, user, false); RulesImportExportObject export = new RulesImportExportObject(); export.setRules(rules);