From 5297d6710e2d4f26ac2b852e70761882655c3ce9 Mon Sep 17 00:00:00 2001 From: Francisco Javier Tirado Sarti <65240126+fjtirado@users.noreply.github.com> Date: Tue, 5 Sep 2023 18:53:40 +0200 Subject: [PATCH] [KOGITO-5823] Supporting uppercase properties (#3210) * [KOGITO-5823] Supporting uppercase properties BeanInstrospector does not work with uppercase. Lets take benefit that the generated code is annotated with JsonProperty and use jackson for serialization/deserialization of Map into Model and Model into Map * [KOGITO-5832] Without using mapper * [KOGITO-5823] Fixing SWF It is better for it to not depend on Models --- .../main/java/org/kie/kogito/MapInput.java | 3 +- .../src/main/java/org/kie/kogito/Models.java | 137 ++++-------------- .../workflow/models/JsonNodeModel.java | 6 + 3 files changed, 37 insertions(+), 109 deletions(-) diff --git a/api/kogito-api/src/main/java/org/kie/kogito/MapInput.java b/api/kogito-api/src/main/java/org/kie/kogito/MapInput.java index 267eb4362a8..b0c143266d2 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/MapInput.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/MapInput.java @@ -29,8 +29,7 @@ public interface MapInput { * in the class */ default MapInput fromMap(Map params) { - Models.fromMap(this, params); - return this; + return Models.fromMap(this, params); } } diff --git a/api/kogito-api/src/main/java/org/kie/kogito/Models.java b/api/kogito-api/src/main/java/org/kie/kogito/Models.java index 2143a48f9d9..5cc8cfed16a 100644 --- a/api/kogito-api/src/main/java/org/kie/kogito/Models.java +++ b/api/kogito-api/src/main/java/org/kie/kogito/Models.java @@ -15,53 +15,33 @@ */ package org.kie.kogito; -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Constructor; -import java.util.Arrays; -import java.util.HashMap; +import java.lang.reflect.Field; +import java.util.LinkedHashMap; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.annotation.JsonProperty; public class Models { - private static final Logger LOGGER = LoggerFactory.getLogger(Models.class); - - private static final String CLASS_PROP = "class"; - private static final String ID_PROP = "id"; - /** - * this prefix is only used when a variable name - * clashes with a predefined Java keyword (e.g. `static`) - */ - private static final String VAR_PREFIX = "v$"; private Models() { } + @SuppressWarnings("squid:S3011") public static Map toMap(Object m) { - try { - Map map = new HashMap<>(); - BeanInfo beanInfo = Introspector.getBeanInfo(m.getClass()); - Map descriptors = descriptorMap(beanInfo); - - for (Map.Entry e : descriptors.entrySet()) { - String k = e.getKey(); - if (isIdentifier(k)) { - LOGGER.trace("Models#toMap: Skipping `id` property for class `{}`", m.getClass().getCanonicalName()); - continue; + Map map = new LinkedHashMap<>(); + for (Field field : m.getClass().getDeclaredFields()) { + JsonProperty jsonAnnotation = field.getAnnotation(JsonProperty.class); + if (jsonAnnotation != null) { + String name = jsonAnnotation.value(); + field.setAccessible(true); + try { + map.put(name, field.get(m)); + } catch (ReflectiveOperationException e) { + throw new ReflectiveModelAccessException(e); } - k = unprefixVar(k); - map.put(k, e.getValue().getReadMethod().invoke(m)); } - return map; - } catch (IntrospectionException | ReflectiveOperationException e) { - throw new ReflectiveModelAccessException(e); } + return map; } public static T fromMap(T m, String id, Map map) { @@ -69,87 +49,30 @@ public static T fromMap(T m, String id, Map map) { return fromMap(m, map); } + @SuppressWarnings("squid:S3011") public static T fromMap(T m, Map map) { - try { - BeanInfo beanInfo = Introspector.getBeanInfo(m.getClass()); - Map descriptors = descriptorMap(beanInfo); - - for (Map.Entry e : descriptors.entrySet()) { - String k = e.getKey(); - k = unprefixVar(k); - if (map.containsKey(k)) { - e.getValue().getWriteMethod().invoke(m, map.get(k)); + for (Field field : m.getClass().getDeclaredFields()) { + JsonProperty jsonAnnotation = field.getAnnotation(JsonProperty.class); + if (jsonAnnotation != null) { + String name = jsonAnnotation.value(); + if (map.containsKey(name)) { + field.setAccessible(true); + try { + field.set(m, map.get(name)); + } catch (ReflectiveOperationException e) { + throw new ReflectiveModelAccessException(e); + } } } - return m; - } catch (IntrospectionException | ReflectiveOperationException e) { - throw new ReflectiveModelAccessException(e); - } - } - - public static T fromMap(Class cls, Map map) { - try { - Constructor constructor = cls.getConstructor(); - T t = constructor.newInstance(); - fromMap(t, map); - return t; - } catch (NoSuchMethodException e) { - throw new ReflectiveModelAccessException( - String.format("Class `%s` must declare an empty constructor.", cls.getCanonicalName()), e); - } catch (ReflectiveOperationException e) { - throw new ReflectiveModelAccessException(e); } + return m; } public static void setId(Object m, String id) { try { - BeanInfo beanInfo = Introspector.getBeanInfo(m.getClass()); - for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) { - if (isIdentifier(pd.getName())) { - pd.getWriteMethod().invoke(m, id); - return; - } - } - // no Id found, throw error - throw new ReflectiveModelAccessException( - String.format( - "No `id` property found for class `%s`. Have you defined getters and setters?", - m.getClass().getCanonicalName())); - } catch (IntrospectionException | ReflectiveOperationException e) { + m.getClass().getMethod("setId", String.class).invoke(m, id); + } catch (ReflectiveOperationException e) { throw new ReflectiveModelAccessException(e); } - } - - public static O convert(I in, O out) { - fromMap(out, toMap(in)); - return out; - } - - /** - * When a process variable name clashes with a predefined - * Java keyword (e.g. `static`), we are prefixing the field - * with `v$` (e.g. `v$static`). - * - * @return the unprefixed variable name - */ - private static String unprefixVar(String k) { - if (k.startsWith(VAR_PREFIX)) { - k = k.substring(VAR_PREFIX.length()); - } - return k; - } - - private static boolean isIdentifier(String k) { - return k.equals(ID_PROP); - } - - private static Map descriptorMap(BeanInfo beanInfo) { - return Arrays.stream(beanInfo.getPropertyDescriptors()) - .filter(pd -> !pd.getName().equals(CLASS_PROP)) - .collect(Collectors.toMap( - PropertyDescriptor::getName, - Function.identity())); - } - } diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/models/JsonNodeModel.java b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/models/JsonNodeModel.java index 61fce5584b5..4152d3e9ad0 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/models/JsonNodeModel.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/models/JsonNodeModel.java @@ -86,6 +86,12 @@ public void fromMap(String id, Map params) { update(id, mutableMap(params)); } + @Override + public MapInput fromMap(Map params) { + update(params); + return this; + } + @Override public Map toMap() { Map map = new HashMap<>();