Repository: syncope Updated Branches: refs/heads/2_0_X ff7e5af0f -> 48ae5566d
JEXL: various enhancements and cleanup, especially for templates Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/48ae5566 Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/48ae5566 Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/48ae5566 Branch: refs/heads/2_0_X Commit: 48ae5566d10cc5678a0de6606c4693aeb7f32359 Parents: ff7e5af Author: Francesco Chicchiriccò <[email protected]> Authored: Tue Nov 21 11:58:53 2017 +0100 Committer: Francesco Chicchiriccò <[email protected]> Committed: Tue Nov 21 11:58:53 2017 +0100 ---------------------------------------------------------------------- .../java/data/JEXLItemTransformerImpl.java | 11 +- .../core/provisioning/java/jexl/JexlUtils.java | 147 +++++++++---------- .../provisioning/java/utils/MappingUtils.java | 6 +- .../provisioning/java/utils/TemplateUtils.java | 36 ++++- 4 files changed, 109 insertions(+), 91 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/48ae5566/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java index ae31618..01f1b7e 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java @@ -25,6 +25,7 @@ import org.apache.commons.jexl3.MapContext; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.to.EntityTO; +import org.apache.syncope.common.lib.to.RealmTO; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.Entity; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; @@ -87,10 +88,16 @@ public class JEXLItemTransformerImpl extends DefaultItemTransformer implements J JexlContext jexlContext = new MapContext(); jexlContext.set("value", value); if (entityTO instanceof AnyTO) { - newValues.add(JexlUtils.evaluate(pullJEXL, (AnyTO) entityTO, jexlContext)); - } else { + JexlUtils.addFieldsToContext((AnyTO) entityTO, jexlContext); + JexlUtils.addAttrTOsToContext(((AnyTO) entityTO).getPlainAttrs(), jexlContext); + JexlUtils.addAttrTOsToContext(((AnyTO) entityTO).getDerAttrs(), jexlContext); + JexlUtils.addAttrTOsToContext(((AnyTO) entityTO).getVirAttrs(), jexlContext); + } else if (entityTO instanceof RealmTO) { + JexlUtils.addFieldsToContext((RealmTO) entityTO, jexlContext); newValues.add(JexlUtils.evaluate(pullJEXL, jexlContext)); } + + newValues.add(JexlUtils.evaluate(pullJEXL, jexlContext)); } return newValues; http://git-wip-us.apache.org/repos/asf/syncope/blob/48ae5566/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java index f68ec60..ddb393f 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java @@ -25,8 +25,11 @@ import java.lang.reflect.Field; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.commons.jexl3.JexlBuilder; import org.apache.commons.jexl3.JexlContext; import org.apache.commons.jexl3.JexlEngine; @@ -38,6 +41,7 @@ import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.to.AnyTO; import org.apache.syncope.common.lib.to.AttrTO; +import org.apache.syncope.common.lib.to.RealmTO; import org.apache.syncope.core.spring.ApplicationContextProvider; import org.apache.syncope.core.provisioning.api.utils.FormatUtils; import org.apache.syncope.core.persistence.api.entity.Any; @@ -57,6 +61,10 @@ public final class JexlUtils { private static final String[] IGNORE_FIELDS = { "password", "clearPassword", "serialVersionUID", "class" }; + private static final Map<Class<?>, Set<PropertyDescriptor>> FIELD_CACHE = + Collections.<Class<?>, Set<PropertyDescriptor>>synchronizedMap( + new HashMap<Class<?>, Set<PropertyDescriptor>>()); + private static JexlEngine JEXL_ENGINE; private static JexlEngine getEngine() { @@ -113,56 +121,77 @@ public final class JexlUtils { return result; } - public static JexlContext addFieldsToContext(final Object object, final JexlContext jexlContext) { - JexlContext context = jexlContext == null ? new MapContext() : jexlContext; + public static void addFieldsToContext(final Object object, final JexlContext jexlContext) { + Set<PropertyDescriptor> cached = FIELD_CACHE.get(object.getClass()); + if (cached == null) { + cached = new HashSet<>(); + FIELD_CACHE.put(object.getClass(), cached); - try { - for (PropertyDescriptor desc : Introspector.getBeanInfo(object.getClass()).getPropertyDescriptors()) { - Class<?> type = desc.getPropertyType(); - String fieldName = desc.getName(); - - if ((!fieldName.startsWith("pc")) - && (!ArrayUtils.contains(IGNORE_FIELDS, fieldName)) - && (!Iterable.class.isAssignableFrom(type)) - && (!type.isArray())) { - - try { - Object fieldValue; - if (desc.getReadMethod() == null) { - final Field field = object.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - fieldValue = field.get(object); - } else { - fieldValue = desc.getReadMethod().invoke(object); - } - - context.set(fieldName, fieldValue == null - ? StringUtils.EMPTY - : (type.equals(Date.class) - ? FormatUtils.format((Date) fieldValue, false) - : fieldValue)); - - LOG.debug("Add field {} with value {}", fieldName, fieldValue); - } catch (Exception iae) { - LOG.error("Reading '{}' value error", fieldName, iae); + try { + for (PropertyDescriptor desc : Introspector.getBeanInfo(object.getClass()).getPropertyDescriptors()) { + if ((!desc.getName().startsWith("pc")) + && (!ArrayUtils.contains(IGNORE_FIELDS, desc.getName())) + && (!Iterable.class.isAssignableFrom(desc.getPropertyType())) + && (!desc.getPropertyType().isArray())) { + + cached.add(desc); } } + } catch (IntrospectionException ie) { + LOG.error("Reading class attributes error", ie); } - } catch (IntrospectionException ie) { - LOG.error("Reading class attributes error", ie); } - if (object instanceof Any) { - Any<?> any = (Any<?>) object; - if (any.getRealm() != null) { - context.set("realm", any.getRealm().getFullPath()); + for (PropertyDescriptor desc : cached) { + String fieldName = desc.getName(); + Class<?> fieldType = desc.getPropertyType(); + + try { + Object fieldValue; + if (desc.getReadMethod() == null) { + final Field field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + fieldValue = field.get(object); + } else { + fieldValue = desc.getReadMethod().invoke(object); + } + fieldValue = fieldValue == null + ? StringUtils.EMPTY + : (fieldType.equals(Date.class) + ? FormatUtils.format((Date) fieldValue, false) + : fieldValue); + + jexlContext.set(fieldName, fieldValue); + + LOG.debug("Add field {} with value {}", fieldName, fieldValue); + } catch (Exception iae) { + LOG.error("Reading '{}' value error", fieldName, iae); } + } + + if (object instanceof Any && ((Any<?>) object).getRealm() != null) { + jexlContext.set("realm", ((Any<?>) object).getRealm().getFullPath()); + } else if (object instanceof AnyTO && ((AnyTO) object).getRealm() != null) { + jexlContext.set("realm", ((AnyTO) object).getRealm()); } else if (object instanceof Realm) { - Realm realm = (Realm) object; - context.set("fullPath", realm.getFullPath()); + jexlContext.set("fullPath", ((Realm) object).getFullPath()); + } else if (object instanceof RealmTO) { + jexlContext.set("fullPath", ((RealmTO) object).getFullPath()); } + } + + public static void addAttrTOsToContext(final Collection<AttrTO> attrs, final JexlContext jexlContext) { + for (AttrTO attr : attrs) { + if (attr.getSchema() != null) { + String expressionValue = attr.getValues().isEmpty() + ? StringUtils.EMPTY + : attr.getValues().get(0); - return context; + LOG.debug("Add attribute {} with value {}", attr.getSchema(), expressionValue); + + jexlContext.set(attr.getSchema(), expressionValue); + } + } } public static void addPlainAttrsToContext( @@ -199,44 +228,6 @@ public final class JexlUtils { return Boolean.parseBoolean(evaluate(mandatoryCondition, jexlContext)); } - public static String evaluate(final String expression, final AnyTO anyTO, final JexlContext context) { - addFieldsToContext(anyTO, context); - - for (AttrTO plainAttr : anyTO.getPlainAttrs()) { - List<String> values = plainAttr.getValues(); - String expressionValue = values.isEmpty() - ? StringUtils.EMPTY - : values.get(0); - - LOG.debug("Add plain attribute {} with value {}", plainAttr.getSchema(), expressionValue); - - context.set(plainAttr.getSchema(), expressionValue); - } - for (AttrTO derAttr : anyTO.getDerAttrs()) { - List<String> values = derAttr.getValues(); - String expressionValue = values.isEmpty() - ? StringUtils.EMPTY - : values.get(0); - - LOG.debug("Add derived attribute {} with value {}", derAttr.getSchema(), expressionValue); - - context.set(derAttr.getSchema(), expressionValue); - } - for (AttrTO virAttr : anyTO.getVirAttrs()) { - List<String> values = virAttr.getValues(); - String expressionValue = values.isEmpty() - ? StringUtils.EMPTY - : values.get(0); - - LOG.debug("Add virtual attribute {} with value {}", virAttr.getSchema(), expressionValue); - - context.set(virAttr.getSchema(), expressionValue); - } - - // Evaluate expression using the context prepared before - return evaluate(expression, context); - } - /** * Private default constructor, for static-only classes. */ http://git-wip-us.apache.org/repos/asf/syncope/blob/48ae5566/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java index ba2c7a4..67f2fe0 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java @@ -87,7 +87,7 @@ public final class MappingUtils { }); } - private static Name evaluateNAME(final String evalConnObjectLink, final String connObjectKey) { + private static Name getName(final String evalConnObjectLink, final String connObjectKey) { // If connObjectLink evaluates to an empty string, just use the provided connObjectKey as Name(), // otherwise evaluated connObjectLink expression is taken as Name(). Name name; @@ -135,7 +135,7 @@ public final class MappingUtils { evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext); } - return evaluateNAME(evalConnObjectLink, connObjectKey); + return getName(evalConnObjectLink, connObjectKey); } /** @@ -165,7 +165,7 @@ public final class MappingUtils { evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext); } - return evaluateNAME(evalConnObjectLink, connObjectKey); + return getName(evalConnObjectLink, connObjectKey); } private static List<ItemTransformer> getItemTransformers( http://git-wip-us.apache.org/repos/asf/syncope/blob/48ae5566/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java index 2b342af..5df7cce 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java @@ -51,13 +51,13 @@ public class TemplateUtils { @Autowired private GroupDAO groupDAO; - private AttrTO evaluateAttr(final AnyTO anyTO, final AttrTO template) { + private AttrTO evaluateAttr(final AttrTO template, final MapContext jexlContext) { AttrTO result = new AttrTO(); result.setSchema(template.getSchema()); if (template.getValues() != null && !template.getValues().isEmpty()) { for (String value : template.getValues()) { - String evaluated = JexlUtils.evaluate(value, anyTO, new MapContext()); + String evaluated = JexlUtils.evaluate(value, jexlContext); if (StringUtils.isNotBlank(evaluated)) { result.getValues().add(evaluated); } @@ -68,8 +68,14 @@ public class TemplateUtils { } private void fill(final AnyTO anyTO, final AnyTO template) { + MapContext jexlContext = new MapContext(); + JexlUtils.addFieldsToContext(anyTO, jexlContext); + JexlUtils.addAttrTOsToContext(anyTO.getPlainAttrs(), jexlContext); + JexlUtils.addAttrTOsToContext(anyTO.getDerAttrs(), jexlContext); + JexlUtils.addAttrTOsToContext(anyTO.getVirAttrs(), jexlContext); + if (template.getRealm() != null) { - String evaluated = JexlUtils.evaluate(template.getRealm(), anyTO, new MapContext()); + String evaluated = JexlUtils.evaluate(template.getRealm(), jexlContext); if (StringUtils.isNotBlank(evaluated)) { anyTO.setRealm(evaluated); } @@ -81,7 +87,11 @@ public class TemplateUtils { && (!currentAttrMap.containsKey(templatePlainAttr.getSchema()) || currentAttrMap.get(templatePlainAttr.getSchema()).getValues().isEmpty())) { - anyTO.getPlainAttrs().add(evaluateAttr(anyTO, templatePlainAttr)); + AttrTO evaluated = evaluateAttr(templatePlainAttr, jexlContext); + if (!evaluated.getValues().isEmpty()) { + anyTO.getPlainAttrs().add(evaluated); + jexlContext.set(evaluated.getSchema(), evaluated.getValues().get(0)); + } } } @@ -98,7 +108,11 @@ public class TemplateUtils { && (!currentAttrMap.containsKey(templateVirAttr.getSchema()) || currentAttrMap.get(templateVirAttr.getSchema()).getValues().isEmpty())) { - anyTO.getVirAttrs().add(evaluateAttr(anyTO, templateVirAttr)); + AttrTO evaluated = evaluateAttr(templateVirAttr, jexlContext); + if (!evaluated.getValues().isEmpty()) { + anyTO.getVirAttrs().add(evaluated); + jexlContext.set(evaluated.getSchema(), evaluated.getValues().get(0)); + } } } @@ -134,19 +148,25 @@ public class TemplateUtils { public <T extends AnyTO> void apply(final T anyTO, final AnyTO template) { fill(anyTO, template); + MapContext jexlContext = new MapContext(); + JexlUtils.addFieldsToContext(anyTO, jexlContext); + JexlUtils.addAttrTOsToContext(anyTO.getPlainAttrs(), jexlContext); + JexlUtils.addAttrTOsToContext(anyTO.getDerAttrs(), jexlContext); + JexlUtils.addAttrTOsToContext(anyTO.getVirAttrs(), jexlContext); + if (template instanceof AnyObjectTO) { fillRelationships((GroupableRelatableTO) anyTO, ((GroupableRelatableTO) template)); fillMemberships((GroupableRelatableTO) anyTO, ((GroupableRelatableTO) template)); } else if (template instanceof UserTO) { if (StringUtils.isNotBlank(((UserTO) template).getUsername())) { - String evaluated = JexlUtils.evaluate(((UserTO) template).getUsername(), anyTO, new MapContext()); + String evaluated = JexlUtils.evaluate(((UserTO) template).getUsername(), jexlContext); if (StringUtils.isNotBlank(evaluated)) { ((UserTO) anyTO).setUsername(evaluated); } } if (StringUtils.isNotBlank(((UserTO) template).getPassword())) { - String evaluated = JexlUtils.evaluate(((UserTO) template).getPassword(), anyTO, new MapContext()); + String evaluated = JexlUtils.evaluate(((UserTO) template).getPassword(), jexlContext); if (StringUtils.isNotBlank(evaluated)) { ((UserTO) anyTO).setPassword(evaluated); } @@ -156,7 +176,7 @@ public class TemplateUtils { fillMemberships((GroupableRelatableTO) anyTO, ((GroupableRelatableTO) template)); } else if (template instanceof GroupTO) { if (StringUtils.isNotBlank(((GroupTO) template).getName())) { - String evaluated = JexlUtils.evaluate(((GroupTO) template).getName(), anyTO, new MapContext()); + String evaluated = JexlUtils.evaluate(((GroupTO) template).getName(), jexlContext); if (StringUtils.isNotBlank(evaluated)) { ((GroupTO) anyTO).setName(evaluated); }
