You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/06/05 17:11:53 UTC

[18/21] syncope git commit: [SYNCOPE-666] All tests are green, time to add more

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index fb01aa4..4a8a35b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -18,14 +18,17 @@
  */
 package org.apache.syncope.core.provisioning.java.data;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -33,6 +36,8 @@ import org.apache.syncope.common.lib.mod.AnyMod;
 import org.apache.syncope.common.lib.mod.AttrMod;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.lib.types.IntMappingType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
@@ -60,21 +65,25 @@ import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.common.lib.types.PropagationByResource;
 import org.apache.syncope.core.misc.ConnObjectUtils;
-import org.apache.syncope.core.provisioning.java.VirAttrHandler;
 import org.apache.syncope.core.misc.MappingUtils;
 import org.apache.syncope.core.misc.jexl.JexlUtils;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.Membership;
 import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.Relationship;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -83,10 +92,18 @@ abstract class AbstractAnyDataBinder {
 
     protected static final Logger LOG = LoggerFactory.getLogger(AbstractAnyDataBinder.class);
 
+    private static final IntMappingType[] FOR_MANDATORY = new IntMappingType[] {
+        IntMappingType.AnyPlainSchema, IntMappingType.AnyDerivedSchema,
+        IntMappingType.UserPlainSchema, IntMappingType.UserDerivedSchema,
+        IntMappingType.GroupPlainSchema, IntMappingType.GroupDerivedSchema };
+
     @Autowired
     protected RealmDAO realmDAO;
 
     @Autowired
+    protected AnyTypeClassDAO anyTypeClassDAO;
+
+    @Autowired
     protected AnyObjectDAO anyObjectDAO;
 
     @Autowired
@@ -129,7 +146,7 @@ abstract class AbstractAnyDataBinder {
     protected AnyUtilsFactory anyUtilsFactory;
 
     @Autowired
-    protected VirAttrHandler virtAttrHander;
+    protected VirAttrHandler virAttrHander;
 
     @Autowired
     protected ConnObjectUtils connObjectUtils;
@@ -138,7 +155,7 @@ abstract class AbstractAnyDataBinder {
         if (StringUtils.isNotBlank(anyMod.getRealm())) {
             Realm newRealm = realmDAO.find(anyMod.getRealm());
             if (newRealm == null) {
-                LOG.warn("Invalid realm specified: {}, ignoring", anyMod.getRealm());
+                LOG.debug("Invalid realm specified: {}, ignoring", anyMod.getRealm());
             } else {
                 any.setRealm(newRealm);
             }
@@ -155,7 +172,6 @@ abstract class AbstractAnyDataBinder {
                 LOG.debug("Ignoring invalid schema {}", schemaName);
             } else if (schema.isReadonly()) {
                 schema = null;
-
                 LOG.debug("Ignoring readonly schema {}", schemaName);
             }
         }
@@ -175,7 +191,7 @@ abstract class AbstractAnyDataBinder {
         return schema;
     }
 
-    protected void fillAttribute(final List<String> values, final AnyUtils anyUtils,
+    private void fillAttribute(final List<String> values, final AnyUtils anyUtils,
             final PlainSchema schema, final PlainAttr<?> attr, final SyncopeClientException invalidValues) {
 
         // if schema is multivalue, all values are considered for addition;
@@ -201,54 +217,57 @@ abstract class AbstractAnyDataBinder {
         }
     }
 
-    private boolean evaluateMandatoryCondition(final AnyUtils anyUtils, final ExternalResource resource,
-            final Any<?, ?, ?> any, final String intAttrName, final IntMappingType intMappingType) {
+    private List<String> evaluateMandatoryCondition(final Provision provision, final Any<?, ?, ?> any) {
+        List<String> missingAttrNames = new ArrayList<>();
+
+        if (provision != null) {
+            for (MappingItem item : provision.getMapping().getItems()) {
+                if (ArrayUtils.contains(FOR_MANDATORY, item.getIntMappingType())
+                        && (item.getPurpose() == MappingPurpose.PROPAGATION
+                        || item.getPurpose() == MappingPurpose.BOTH)) {
 
-        boolean result = false;
+                    List<PlainAttrValue> values = MappingUtils.getIntValues(
+                            provision, item, Collections.<Any<?, ?, ?>>singletonList(any), null, null);
+                    if ((values == null || values.isEmpty())
+                            && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any)) {
 
-        Collection<MappingItem> mappings = MappingUtils.getMatchingMappingItems(
-                anyUtils.getMappingItems(resource.getProvision(any.getType()), MappingPurpose.PROPAGATION),
-                intAttrName, intMappingType);
-        for (Iterator<MappingItem> itor = mappings.iterator(); itor.hasNext() && !result;) {
-            MappingItem mapping = itor.next();
-            result |= JexlUtils.evaluateMandatoryCondition(mapping.getMandatoryCondition(), any);
+                        missingAttrNames.add(item.getIntAttrName());
+                    }
+                }
+            }
         }
 
-        return result;
+        return missingAttrNames;
     }
 
-    private boolean evaluateMandatoryCondition(final AnyUtils anyUtils,
-            final Any<?, ?, ?> any, final String intAttrName, final IntMappingType intMappingType) {
+    private SyncopeClientException checkMandatoryOnResources(
+            final Any<?, ?, ?> any, final Set<ExternalResource> resources) {
 
-        boolean result = false;
+        SyncopeClientException reqValMissing = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
 
-        Iterable<? extends ExternalResource> iterable = any instanceof User
-                ? userDAO.findAllResources((User) any)
-                : any instanceof Group
-                        ? ((Group) any).getResources()
-                        : Collections.<ExternalResource>emptySet();
-
-        for (Iterator<? extends ExternalResource> itor = iterable.iterator(); itor.hasNext() && !result;) {
-            ExternalResource resource = itor.next();
-            if (resource.isEnforceMandatoryCondition()) {
-                result |= evaluateMandatoryCondition(
-                        anyUtils, resource, any, intAttrName, intMappingType);
+        for (ExternalResource resource : resources) {
+            Provision provision = resource.getProvision(any.getType());
+            if (resource.isEnforceMandatoryCondition() && provision != null) {
+                List<String> missingAttrNames = evaluateMandatoryCondition(provision, any);
+                if (!missingAttrNames.isEmpty()) {
+                    LOG.error("Mandatory schemas {} not provided with values", missingAttrNames);
+
+                    reqValMissing.getElements().addAll(missingAttrNames);
+                }
             }
         }
 
-        return result;
+        return reqValMissing;
     }
 
-    private SyncopeClientException checkMandatory(final AnyUtils anyUtils, final Any<?, ?, ?> any) {
+    private SyncopeClientException checkMandatory(final Any<?, ?, ?> any) {
         SyncopeClientException reqValMissing = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
 
         // Check if there is some mandatory schema defined for which no value has been provided
         for (PlainSchema schema : any.getAllowedPlainSchemas()) {
             if (any.getPlainAttr(schema.getKey()) == null
                     && !schema.isReadonly()
-                    && (JexlUtils.evaluateMandatoryCondition(schema.getMandatoryCondition(), any)
-                    || evaluateMandatoryCondition(anyUtils, any, schema.getKey(),
-                            anyUtils.plainIntMappingType()))) {
+                    && (JexlUtils.evaluateMandatoryCondition(schema.getMandatoryCondition(), any))) {
 
                 LOG.error("Mandatory schema " + schema.getKey() + " not provided with values");
 
@@ -256,30 +275,21 @@ abstract class AbstractAnyDataBinder {
             }
         }
 
-        for (DerSchema derSchema : any.getAllowedDerSchemas()) {
-            if (any.getDerAttr(derSchema.getKey()) == null
-                    && evaluateMandatoryCondition(anyUtils, any, derSchema.getKey(),
-                            anyUtils.derIntMappingType())) {
-
-                LOG.error("Mandatory derived schema " + derSchema.getKey() + " does not evaluate to any value");
-
-                reqValMissing.getElements().add(derSchema.getKey());
-            }
-        }
-
-        for (VirSchema virSchema : any.getAllowedVirSchemas()) {
-            if (any.getVirAttr(virSchema.getKey()) == null
-                    && !virSchema.isReadonly()
-                    && evaluateMandatoryCondition(anyUtils, any, virSchema.getKey(),
-                            anyUtils.virIntMappingType())) {
+        return reqValMissing;
+    }
 
-                LOG.error("Mandatory virtual schema " + virSchema.getKey() + " not provided with values");
+    private Set<ExternalResource> getAllResources(final Any<?, ?, ?> any) {
+        Set<ExternalResource> resources = new HashSet<>();
 
-                reqValMissing.getElements().add(virSchema.getKey());
-            }
+        if (any instanceof User) {
+            resources.addAll(userDAO.findAllResources((User) any));
+        } else if (any instanceof Group) {
+            resources.addAll(((Group) any).getResources());
+        } else if (any instanceof AnyObject) {
+            resources.addAll(anyObjectDAO.findAllResources((AnyObject) any));
         }
 
-        return reqValMissing;
+        return resources;
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -288,9 +298,29 @@ abstract class AbstractAnyDataBinder {
 
         PropagationByResource propByRes = new PropagationByResource();
 
+        // 1. anyTypeClass to be removed
+        for (String className : anyMod.getAuxClassesToRemove()) {
+            AnyTypeClass auxClass = anyTypeClassDAO.find(className);
+            if (auxClass == null) {
+                LOG.debug("Invalid " + AnyTypeClass.class.getSimpleName() + "{}, ignoring...", auxClass);
+            } else {
+                any.remove(auxClass);
+            }
+        }
+
+        // 2. anyTypeClass to be added
+        for (String className : anyMod.getAuxClassesToAdd()) {
+            AnyTypeClass auxClass = anyTypeClassDAO.find(className);
+            if (auxClass == null) {
+                LOG.debug("Invalid " + AnyTypeClass.class.getSimpleName() + "{}, ignoring...", auxClass);
+            } else {
+                any.add(auxClass);
+            }
+        }
+
         SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
 
-        // 1. resources to be removed
+        // 3. resources to be removed
         for (String resourceToBeRemoved : anyMod.getResourcesToRemove()) {
             ExternalResource resource = resourceDAO.find(resourceToBeRemoved);
             if (resource != null) {
@@ -301,7 +331,7 @@ abstract class AbstractAnyDataBinder {
 
         LOG.debug("Resources to be removed:\n{}", propByRes);
 
-        // 2. resources to be added
+        // 4. resources to be added
         for (String resourceToBeAdded : anyMod.getResourcesToAdd()) {
             ExternalResource resource = resourceDAO.find(resourceToBeAdded);
             if (resource != null) {
@@ -312,16 +342,9 @@ abstract class AbstractAnyDataBinder {
 
         LOG.debug("Resources to be added:\n{}", propByRes);
 
-        Set<ExternalResource> externalResources = new HashSet<>();
-        if (any instanceof User) {
-            externalResources.addAll(userDAO.findAllResources((User) any));
-        } else if (any instanceof Group) {
-            externalResources.addAll(((Group) any).getResources());
-        } else if (any instanceof AnyObject) {
-            externalResources.addAll(anyObjectDAO.findAllResources((AnyObject) any));
-        }
+        Set<ExternalResource> resources = getAllResources(any);
 
-        // 3. attributes to be removed
+        // 5. attributes to be removed
         for (String attributeToBeRemoved : anyMod.getPlainAttrsToRemove()) {
             PlainSchema schema = getPlainSchema(attributeToBeRemoved);
             if (schema != null) {
@@ -344,7 +367,7 @@ abstract class AbstractAnyDataBinder {
                     }
                 }
 
-                for (ExternalResource resource : externalResources) {
+                for (ExternalResource resource : resources) {
                     for (MappingItem mapItem : anyUtils.getMappingItems(
                             resource.getProvision(any.getType()), MappingPurpose.PROPAGATION)) {
 
@@ -364,7 +387,7 @@ abstract class AbstractAnyDataBinder {
 
         LOG.debug("Attributes to be removed:\n{}", propByRes);
 
-        // 4. attributes to be updated
+        // 6. attributes to be updated
         for (AttrMod attributeMod : anyMod.getPlainAttrsToUpdate()) {
             PlainSchema schema = getPlainSchema(attributeMod.getSchema());
             PlainAttr attr = null;
@@ -372,19 +395,15 @@ abstract class AbstractAnyDataBinder {
                 attr = any.getPlainAttr(schema.getKey());
                 if (attr == null) {
                     attr = anyUtils.newPlainAttr();
+                    attr.setOwner(any);
                     attr.setSchema(schema);
-                    if (attr.getSchema() == null) {
-                        LOG.debug("Ignoring {} because no valid schema or template was found", attributeMod);
-                    } else {
-                        attr.setOwner(any);
-                        any.add(attr);
-                    }
+                    any.add(attr);
                 }
             }
 
             if (schema != null && attr != null && attr.getSchema() != null) {
-                virtAttrHander.updateOnResourcesIfMappingMatches(any, anyUtils, schema.getKey(),
-                        externalResources, anyUtils.plainIntMappingType(), propByRes);
+                virAttrHander.updateOnResourcesIfMappingMatches(any, anyUtils, schema.getKey(),
+                        resources, anyUtils.plainIntMappingType(), propByRes);
 
                 // 1.1 remove values
                 Set<Long> valuesToBeRemoved = new HashSet<>();
@@ -429,7 +448,7 @@ abstract class AbstractAnyDataBinder {
 
         LOG.debug("Attributes to be updated:\n{}", propByRes);
 
-        // 5. derived attributes to be removed
+        // 7. derived attributes to be removed
         for (String derAttrToBeRemoved : anyMod.getDerAttrsToRemove()) {
             DerSchema derSchema = getDerSchema(derAttrToBeRemoved);
             if (derSchema != null) {
@@ -440,7 +459,7 @@ abstract class AbstractAnyDataBinder {
                     derAttrDAO.delete(derAttr);
                 }
 
-                for (ExternalResource resource : externalResources) {
+                for (ExternalResource resource : resources) {
                     for (MappingItem mapItem : anyUtils.getMappingItems(
                             resource.getProvision(any.getType()), MappingPurpose.PROPAGATION)) {
 
@@ -463,19 +482,18 @@ abstract class AbstractAnyDataBinder {
 
         LOG.debug("Derived attributes to be removed:\n{}", propByRes);
 
-        // 6. derived attributes to be added
+        // 8. derived attributes to be added
         for (String derAttrToBeAdded : anyMod.getDerAttrsToAdd()) {
             DerSchema derSchema = getDerSchema(derAttrToBeAdded);
             if (derSchema != null) {
-                virtAttrHander.updateOnResourcesIfMappingMatches(any, anyUtils, derSchema.getKey(),
-                        externalResources, anyUtils.derIntMappingType(), propByRes);
+                virAttrHander.updateOnResourcesIfMappingMatches(any, anyUtils, derSchema.getKey(),
+                        resources, anyUtils.derIntMappingType(), propByRes);
 
-                DerAttr derAttr = anyUtils.newDerAttr();
-                derAttr.setSchema(derSchema);
-                if (derAttr.getSchema() == null) {
-                    LOG.debug("Ignoring {} because no valid schema or template was found", derAttrToBeAdded);
-                } else {
+                DerAttr derAttr = any.getDerAttr(derSchema.getKey());
+                if (derAttr == null) {
+                    derAttr = anyUtils.newDerAttr();
                     derAttr.setOwner(any);
+                    derAttr.setSchema(derSchema);
                     any.add(derAttr);
                 }
             }
@@ -483,8 +501,11 @@ abstract class AbstractAnyDataBinder {
 
         LOG.debug("Derived attributes to be added:\n{}", propByRes);
 
-        // Finally, check if mandatory values are missing
-        SyncopeClientException requiredValuesMissing = checkMandatory(anyUtils, any);
+        SyncopeClientException requiredValuesMissing = checkMandatory(any);
+        if (!requiredValuesMissing.isEmpty()) {
+            scce.addException(requiredValuesMissing);
+        }
+        requiredValuesMissing = checkMandatoryOnResources(any, resources);
         if (!requiredValuesMissing.isEmpty()) {
             scce.addException(requiredValuesMissing);
         }
@@ -501,6 +522,17 @@ abstract class AbstractAnyDataBinder {
     protected void fill(final Any any, final AnyTO anyTO,
             final AnyUtils anyUtils, final SyncopeClientCompositeException scce) {
 
+        // 0. aux classes
+        any.getAuxClasses().clear();
+        for (String className : anyTO.getAuxClasses()) {
+            AnyTypeClass auxClass = anyTypeClassDAO.find(className);
+            if (auxClass == null) {
+                LOG.debug("Invalid " + AnyTypeClass.class.getSimpleName() + "{}, ignoring...", auxClass);
+            } else {
+                any.add(auxClass);
+            }
+        }
+
         // 1. attributes
         SyncopeClientException invalidValues = SyncopeClientException.build(ClientExceptionType.InvalidValues);
 
@@ -508,7 +540,6 @@ abstract class AbstractAnyDataBinder {
         for (AttrTO attributeTO : anyTO.getPlainAttrs()) {
             if (attributeTO.getValues() != null && !attributeTO.getValues().isEmpty()) {
                 PlainSchema schema = getPlainSchema(attributeTO.getSchema());
-
                 if (schema != null) {
                     PlainAttr attr = any.getPlainAttr(schema.getKey());
                     if (attr == null) {
@@ -534,7 +565,6 @@ abstract class AbstractAnyDataBinder {
         // 2. derived attributes
         for (AttrTO attributeTO : anyTO.getDerAttrs()) {
             DerSchema derSchema = getDerSchema(attributeTO.getSchema());
-
             if (derSchema != null) {
                 DerAttr derAttr = anyUtils.newDerAttr();
                 derAttr.setOwner(any);
@@ -545,8 +575,7 @@ abstract class AbstractAnyDataBinder {
 
         // 3. virtual attributes
         for (AttrTO vattrTO : anyTO.getVirAttrs()) {
-            VirSchema virSchema = virtAttrHander.getVirSchema(vattrTO.getSchema());
-
+            VirSchema virSchema = virAttrHander.getVirSchema(vattrTO.getSchema());
             if (virSchema != null) {
                 VirAttr virAttr = anyUtils.newVirAttr();
                 virAttr.setOwner(any);
@@ -555,27 +584,32 @@ abstract class AbstractAnyDataBinder {
             }
         }
 
-        virtAttrHander.fillVirtual(any, anyTO.getVirAttrs(), anyUtils);
+        SyncopeClientException requiredValuesMissing = checkMandatory(any);
+        if (!requiredValuesMissing.isEmpty()) {
+            scce.addException(requiredValuesMissing);
+        }
+
+        virAttrHander.fillVirtual(any, anyTO.getVirAttrs());
 
         // 4. realm & resources
         Realm realm = realmDAO.find(anyTO.getRealm());
         if (realm == null) {
             SyncopeClientException noRealm = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
-            noRealm.getElements().add(
-                    "Invalid or null realm specified: " + anyTO.getRealm());
+            noRealm.getElements().add("Invalid or null realm specified: " + anyTO.getRealm());
             scce.addException(noRealm);
         }
         any.setRealm(realm);
 
         for (String resourceName : anyTO.getResources()) {
             ExternalResource resource = resourceDAO.find(resourceName);
-
-            if (resource != null) {
+            if (resource == null) {
+                LOG.debug("Invalid " + ExternalResource.class.getSimpleName() + "{}, ignoring...", resourceName);
+            } else {
                 any.add(resource);
             }
         }
 
-        SyncopeClientException requiredValuesMissing = checkMandatory(anyUtils, any);
+        requiredValuesMissing = checkMandatoryOnResources(any, getAllResources(any));
         if (!requiredValuesMissing.isEmpty()) {
             scce.addException(requiredValuesMissing);
         }
@@ -588,11 +622,22 @@ abstract class AbstractAnyDataBinder {
 
     protected void fillTO(final AnyTO anyTO,
             final String realmFullPath,
+            final Collection<? extends AnyTypeClass> auxClasses,
             final Collection<? extends PlainAttr<?>> attrs,
             final Collection<? extends DerAttr<?>> derAttrs,
             final Collection<? extends VirAttr<?>> virAttrs,
             final Collection<? extends ExternalResource> resources) {
 
+        anyTO.setRealm(realmFullPath);
+
+        CollectionUtils.collect(auxClasses, new Transformer<AnyTypeClass, String>() {
+
+            @Override
+            public String transform(final AnyTypeClass role) {
+                return role.getKey();
+            }
+        }, anyTO.getAuxClasses());
+
         AttrTO attributeTO;
         for (PlainAttr<?> attr : attrs) {
             attributeTO = new AttrTO();
@@ -621,12 +666,29 @@ abstract class AbstractAnyDataBinder {
             anyTO.getVirAttrs().add(attributeTO);
         }
 
-        anyTO.setRealm(realmFullPath);
         for (ExternalResource resource : resources) {
             anyTO.getResources().add(resource.getKey());
         }
     }
 
+    protected RelationshipTO getRelationshipTO(final Relationship<? extends Any<?, ?, ?>, AnyObject> relationship) {
+        RelationshipTO relationshipTO = new RelationshipTO();
+        relationshipTO.setLeftKey(relationship.getLeftEnd().getKey());
+        relationshipTO.setLeftType(relationship.getLeftEnd().getType().getKey());
+        relationshipTO.setRightKey(relationship.getRightEnd().getKey());
+        relationshipTO.setRightType(relationship.getRightEnd().getType().getKey());
+        return relationshipTO;
+    }
+
+    protected MembershipTO getMembershipTO(final Membership<? extends Any<?, ?, ?>> membership) {
+        MembershipTO membershipTO = new MembershipTO();
+        membershipTO.setLeftKey(membership.getLeftEnd().getKey());
+        membershipTO.setLeftType(membership.getLeftEnd().getType().getKey());
+        membershipTO.setRightKey(membership.getRightEnd().getKey());
+        membershipTO.setGroupName(membership.getRightEnd().getName());
+        return membershipTO;
+    }
+
     protected Map<String, String> getConnObjectKeys(final Any<?, ?, ?> any) {
         Map<String, String> connObjectKeys = new HashMap<>();
 
@@ -637,7 +699,7 @@ abstract class AbstractAnyDataBinder {
                         : ((Group) any).getResources();
         for (ExternalResource resource : iterable) {
             Provision provision = resource.getProvision(any.getType());
-            if (provision.getMapping() != null) {
+            if (provision != null && provision.getMapping() != null) {
                 MappingItem connObjectKeyItem = anyUtilsFactory.getInstance(any).getConnObjectKeyItem(provision);
                 if (connObjectKeyItem == null) {
                     throw new NotFoundException(

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index eb7586d..462a488 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -31,11 +31,13 @@ import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.mod.AnyObjectMod;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.core.misc.spring.BeanUtils;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
+import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
@@ -47,7 +49,8 @@ import org.springframework.transaction.annotation.Transactional;
 public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements AnyObjectDataBinder {
 
     private static final String[] IGNORE_PROPERTIES = {
-        "realm", "memberships", "plainAttrs", "derAttrs", "virAttrs", "resources"
+        "type", "realm", "auxClasses", "relationships", "memberships", "dynGroups",
+        "plainAttrs", "derAttrs", "virAttrs", "resources"
     };
 
     @Transactional(readOnly = true)
@@ -62,20 +65,29 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
 
         BeanUtils.copyProperties(anyObject, anyObjectTO, IGNORE_PROPERTIES);
 
-        connObjectUtils.retrieveVirAttrValues(anyObject);
-        fillTO(anyObjectTO, anyObject.getRealm().getFullPath(),
+        virAttrHander.retrieveVirAttrValues(anyObject);
+        fillTO(anyObjectTO, anyObject.getRealm().getFullPath(), anyObject.getAuxClasses(),
                 anyObject.getPlainAttrs(), anyObject.getDerAttrs(), anyObject.getVirAttrs(),
                 anyObjectDAO.findAllResources(anyObject));
 
-        for (AMembership membership : anyObject.getMemberships()) {
-            MembershipTO membershipTO = new MembershipTO();
+        // relationships
+        CollectionUtils.collect(anyObject.getRelationships(), new Transformer<ARelationship, RelationshipTO>() {
 
-            membershipTO.setKey(membership.getKey());
-            membershipTO.setRightKey(membership.getRightEnd().getKey());
-            membershipTO.setGroupName(membership.getRightEnd().getName());
+            @Override
+            public RelationshipTO transform(final ARelationship relationship) {
+                return AnyObjectDataBinderImpl.this.getRelationshipTO(relationship);
+            }
 
-            anyObjectTO.getMemberships().add(membershipTO);
-        }
+        }, anyObjectTO.getRelationships());
+
+        // memberships
+        CollectionUtils.collect(anyObject.getMemberships(), new Transformer<AMembership, MembershipTO>() {
+
+            @Override
+            public MembershipTO transform(final AMembership membership) {
+                return AnyObjectDataBinderImpl.this.getMembershipTO(membership);
+            }
+        }, anyObjectTO.getMemberships());
 
         // dynamic memberships
         CollectionUtils.collect(anyObjectDAO.findDynGroupMemberships(anyObject), new Transformer<Group, Long>() {
@@ -93,6 +105,29 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
     public void create(final AnyObject anyObject, final AnyObjectTO anyObjectTO) {
         SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 
+        // relationships
+        for (RelationshipTO relationshipTO : anyObjectTO.getRelationships()) {
+            AnyObject otherEnd = anyObjectDAO.find(relationshipTO.getRightKey());
+
+            if (otherEnd == null) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
+                }
+            } else {
+                ARelationship relationship = null;
+                if (anyObject.getKey() != null) {
+                    relationship = anyObject.getRelationship(otherEnd.getKey());
+                }
+                if (relationship == null) {
+                    relationship = entityFactory.newEntity(ARelationship.class);
+                    relationship.setRightEnd(otherEnd);
+                    relationship.setLeftEnd(anyObject);
+
+                    anyObject.add(relationship);
+                }
+            }
+        }
+
         // memberships
         for (MembershipTO membershipTO : anyObjectTO.getMemberships()) {
             Group group = groupDAO.find(membershipTO.getRightKey());
@@ -137,11 +172,44 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
         // attributes, derived attributes, virtual attributes and resources
         propByRes.merge(fill(anyObject, anyObjectMod, anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT), scce));
 
-        // store the group ids of membership required to be added
-        Set<Long> membershipToBeAddedGroupKeys = new HashSet<>(anyObjectMod.getMembershipsToAdd());
+        Set<String> toBeDeprovisioned = new HashSet<>();
+        Set<String> toBeProvisioned = new HashSet<>();
+
+        // relationships to be removed
+        for (Long anyObjectKey : anyObjectMod.getRelationshipsToRemove()) {
+            LOG.debug("Relationship to be removed for any object {}", anyObjectKey);
+
+            ARelationship relationship = anyObject.getRelationship(anyObjectKey);
+            if (relationship == null) {
+                LOG.warn("Invalid anyObject key specified for relationship to be removed: {}", anyObjectKey);
+            } else {
+                if (!anyObjectMod.getRelationshipsToAdd().contains(anyObjectKey)) {
+                    anyObject.remove(relationship);
+                    toBeDeprovisioned.addAll(relationship.getRightEnd().getResourceNames());
+                }
+            }
+        }
+
+        // relationships to be added
+        for (Long anyObjectKey : anyObjectMod.getRelationshipsToAdd()) {
+            LOG.debug("Relationship to be added for any object {}", anyObjectKey);
+
+            AnyObject otherEnd = anyObjectDAO.find(anyObjectKey);
+            if (otherEnd == null) {
+                LOG.debug("Ignoring invalid any object {}", anyObjectKey);
+            } else {
+                ARelationship relationship = anyObject.getRelationship(otherEnd.getKey());
+                if (relationship == null) {
+                    relationship = entityFactory.newEntity(ARelationship.class);
+                    relationship.setRightEnd(otherEnd);
+                    relationship.setLeftEnd(anyObject);
+
+                    anyObject.add(relationship);
 
-        final Set<String> toBeDeprovisioned = new HashSet<>();
-        final Set<String> toBeProvisioned = new HashSet<>();
+                    toBeProvisioned.addAll(otherEnd.getResourceNames());
+                }
+            }
+        }
 
         // memberships to be removed
         for (Long groupKey : anyObjectMod.getMembershipsToRemove()) {
@@ -151,9 +219,8 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
             if (membership == null) {
                 LOG.warn("Invalid group key specified for membership to be removed: {}", groupKey);
             } else {
-                if (membershipToBeAddedGroupKeys.contains(membership.getRightEnd().getKey())) {
+                if (!anyObjectMod.getMembershipsToAdd().contains(groupKey)) {
                     anyObject.remove(membership);
-                } else {
                     toBeDeprovisioned.addAll(membership.getRightEnd().getResourceNames());
                 }
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java
new file mode 100644
index 0000000..5917190
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeClassDataBinderImpl.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.to.AnyTypeClassTO;
+import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
+import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.provisioning.api.data.AnyTypeClassDataBinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AnyTypeClassDataBinderImpl implements AnyTypeClassDataBinder {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnyTypeClassDataBinder.class);
+
+    @Autowired
+    private PlainSchemaDAO plainSchemaDAO;
+
+    @Autowired
+    private DerSchemaDAO derSchemaDAO;
+
+    @Autowired
+    private VirSchemaDAO virSchemaDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Override
+    public AnyTypeClass create(final AnyTypeClassTO anyTypeClassTO) {
+        AnyTypeClass anyTypeClass = entityFactory.newEntity(AnyTypeClass.class);
+        update(anyTypeClass, anyTypeClassTO);
+        return anyTypeClass;
+    }
+
+    @Override
+    public void update(final AnyTypeClass anyTypeClass, final AnyTypeClassTO anyTypeClassTO) {
+        if (anyTypeClass.getKey() == null) {
+            anyTypeClass.setKey(anyTypeClassTO.getKey());
+        }
+
+        anyTypeClass.getPlainSchemas().clear();
+        for (String schemaName : anyTypeClassTO.getPlainSchemas()) {
+            PlainSchema schema = plainSchemaDAO.find(schemaName);
+            if (schema == null) {
+                LOG.debug("Invalid " + PlainSchema.class.getSimpleName() + "{}, ignoring...", schemaName);
+            } else {
+                anyTypeClass.add(schema);
+            }
+        }
+
+        anyTypeClass.getDerSchemas().clear();
+        for (String schemaName : anyTypeClassTO.getDerSchemas()) {
+            DerSchema schema = derSchemaDAO.find(schemaName);
+            if (schema == null) {
+                LOG.debug("Invalid " + DerSchema.class.getSimpleName() + "{}, ignoring...", schemaName);
+            } else {
+                anyTypeClass.add(schema);
+            }
+        }
+
+        anyTypeClass.getVirSchemas().clear();
+        for (String schemaName : anyTypeClassTO.getVirSchemas()) {
+            VirSchema schema = virSchemaDAO.find(schemaName);
+            if (schema == null) {
+                LOG.debug("Invalid " + VirSchema.class.getSimpleName() + "{}, ignoring...", schemaName);
+            } else {
+                anyTypeClass.add(schema);
+            }
+        }
+    }
+
+    @Override
+    public AnyTypeClassTO getAnyTypeClassTO(final AnyTypeClass anyTypeClass) {
+        AnyTypeClassTO anyTypeClassTO = new AnyTypeClassTO();
+
+        anyTypeClassTO.setKey(anyTypeClass.getKey());
+        for (PlainSchema schema : anyTypeClass.getPlainSchemas()) {
+            anyTypeClassTO.getPlainSchemas().add(schema.getKey());
+        }
+        for (DerSchema schema : anyTypeClass.getDerSchemas()) {
+            anyTypeClassTO.getDerSchemas().add(schema.getKey());
+        }
+        for (VirSchema schema : anyTypeClass.getVirSchemas()) {
+            anyTypeClassTO.getVirSchemas().add(schema.getKey());
+        }
+
+        return anyTypeClassTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
new file mode 100644
index 0000000..0e85d3a
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyTypeDataBinderImpl.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AnyTypeTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
+import org.apache.syncope.core.provisioning.api.data.AnyTypeDataBinder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AnyTypeDataBinderImpl implements AnyTypeDataBinder {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AnyTypeDataBinder.class);
+
+    @Autowired
+    private AnyTypeClassDAO anyTypeClassDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Override
+    public AnyType create(final AnyTypeTO anyTypeTO) {
+        AnyType anyType = entityFactory.newEntity(AnyType.class);
+        update(anyType, anyTypeTO);
+        return anyType;
+    }
+
+    @Override
+    public void update(final AnyType anyType, final AnyTypeTO anyTypeTO) {
+        if (anyType.getKey() == null) {
+            anyType.setKey(anyTypeTO.getKey());
+        }
+        if (anyType.getKind() == null) {
+            anyType.setKind(anyTypeTO.getKind());
+        }
+        if (anyType.getKind() != anyTypeTO.getKind()) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidAnyType);
+            sce.getElements().add("AnyTypeKind cannot be changed");
+            throw sce;
+        }
+
+        anyType.getClasses().clear();
+        for (String anyTypeClassName : anyTypeTO.getClasses()) {
+            AnyTypeClass anyTypeClass = anyTypeClassDAO.find(anyTypeClassName);
+            if (anyTypeClass == null) {
+                LOG.debug("Invalid " + AnyTypeClass.class.getSimpleName() + "{}, ignoring...", anyTypeClassName);
+            } else {
+                anyType.add(anyTypeClass);
+            }
+        }
+    }
+
+    @Override
+    public AnyTypeTO getAnyTypeTO(final AnyType anyType) {
+        AnyTypeTO anyTypeTO = new AnyTypeTO();
+
+        anyTypeTO.setKey(anyType.getKey());
+        anyTypeTO.setKind(anyType.getKind());
+        for (AnyTypeClass anyTypeClass : anyType.getClasses()) {
+            anyTypeTO.getClasses().add(anyTypeClass.getKey());
+        }
+
+        return anyTypeTO;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
index fe5668d..3cf65fd 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConfigurationDataBinderImpl.java
@@ -47,8 +47,8 @@ public class ConfigurationDataBinderImpl extends AbstractAnyDataBinder implement
         ConfTO confTO = new ConfTO();
         confTO.setKey(conf.getKey());
 
-        fillTO(confTO, null, conf.getPlainAttrs(),
-                conf.getDerAttrs(), conf.getVirAttrs(), Collections.<ExternalResource>emptySet());
+        fillTO(confTO, null, conf.getAuxClasses(),
+                conf.getPlainAttrs(), conf.getDerAttrs(), conf.getVirAttrs(), Collections.<ExternalResource>emptySet());
 
         return confTO;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
index a900e70..a393fda 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java
@@ -105,7 +105,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
         if (groupTO.getADynMembershipCond() != null) {
             setDynMembership(group, AnyTypeKind.ANY_OBJECT, groupTO.getADynMembershipCond());
         }
-        if (groupTO.getADynMembershipCond() != null) {
+        if (groupTO.getUDynMembershipCond() != null) {
             setDynMembership(group, AnyTypeKind.USER, groupTO.getUDynMembershipCond());
         }
 
@@ -188,7 +188,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
     @Transactional(readOnly = true)
     @Override
     public GroupTO getGroupTO(final Group group) {
-        connObjectUtils.retrieveVirAttrValues(group);
+        virAttrHander.retrieveVirAttrValues(group);
 
         GroupTO groupTO = new GroupTO();
 
@@ -208,7 +208,7 @@ public class GroupDataBinderImpl extends AbstractAnyDataBinder implements GroupD
             groupTO.setGroupOwner(group.getGroupOwner().getKey());
         }
 
-        fillTO(groupTO, group.getRealm().getFullPath(),
+        fillTO(groupTO, group.getRealm().getFullPath(), group.getAuxClasses(),
                 group.getPlainAttrs(), group.getDerAttrs(), group.getVirAttrs(), group.getResources());
 
         if (group.getADynMembership() != null) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
index eec346e..0d7ee47 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
@@ -19,6 +19,8 @@
 package org.apache.syncope.core.provisioning.java.data;
 
 import java.util.Map;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder;
 import org.apache.syncope.common.lib.to.NotificationTO;
@@ -72,23 +74,35 @@ public class NotificationDataBinderImpl implements NotificationDataBinder {
     @Override
     public void update(final Notification notification, final NotificationTO notificationTO) {
         BeanUtils.copyProperties(notificationTO, notification, IGNORE_PROPERTIES);
+        notification.setRecipients(notificationTO.getRecipients());
 
-        notification.getAbouts().clear();
+        // 1. add or update all (valid) abouts from TO
         for (Map.Entry<String, String> entry : notificationTO.getAbouts().entrySet()) {
             if (StringUtils.isNotBlank(entry.getValue())) {
                 AnyType anyType = anyTypeDAO.find(entry.getKey());
                 if (anyType == null) {
-                    LOG.warn("Invalid AnyType {} specified, ignoring...", entry.getKey());
+                    LOG.debug("Invalid AnyType {} specified, ignoring...", entry.getKey());
                 } else {
-                    AnyAbout about = entityFactory.newEntity(AnyAbout.class);
-                    about.setAnyType(anyType);
-                    about.setNotification(notification);
-
-                    notification.add(about);
+                    AnyAbout about = notification.getAbout(anyType);
+                    if (about == null) {
+                        about = entityFactory.newEntity(AnyAbout.class);
+                        about.setAnyType(anyType);
+                        about.setNotification(notification);
+
+                        notification.add(about);
+                    }
+                    about.set(entry.getValue());
                 }
             }
         }
 
-        notification.setRecipients(notificationTO.getRecipients());
+        // 2. remove all abouts not contained in the TO
+        CollectionUtils.filter(notification.getAbouts(), new Predicate<AnyAbout>() {
+
+            @Override
+            public boolean evaluate(final AnyAbout anyAbout) {
+                return notificationTO.getAbouts().containsKey(anyAbout.getAnyType().getKey());
+            }
+        });
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
index 20b3c58..cb8f979 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
@@ -22,6 +22,8 @@ import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -89,8 +91,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
 
         resource.setKey(resourceTO.getKey());
 
-        if (resourceTO.getConnectorId() != null) {
-            ConnInstance connector = connInstanceDAO.find(resourceTO.getConnectorId());
+        if (resourceTO.getConnector() != null) {
+            ConnInstance connector = connInstanceDAO.find(resourceTO.getConnector());
             resource.setConnector(connector);
 
             if (!connector.getResources().contains(resource)) {
@@ -108,10 +110,11 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
 
         resource.setPropagationMode(resourceTO.getPropagationMode());
 
+        // 1. add or update all (valid) provisions from TO
         for (ProvisionTO provisionTO : resourceTO.getProvisions()) {
             AnyType anyType = anyTypeDAO.find(provisionTO.getAnyType());
             if (anyType == null) {
-                LOG.warn("Invalid type specified {}, ignoring...", provisionTO.getAnyType());
+                LOG.debug("Invalid AnyType specified {}, ignoring...", provisionTO.getAnyType());
             } else {
                 Provision provision = resource.getProvision(anyType);
                 if (provision == null) {
@@ -135,14 +138,28 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
                 if (provisionTO.getMapping() == null) {
                     provision.setMapping(null);
                 } else {
-                    Mapping mapping = entityFactory.newEntity(Mapping.class);
-                    mapping.setProvision(provision);
-                    provision.setMapping(mapping);
+                    Mapping mapping = provision.getMapping();
+                    if (mapping == null) {
+                        mapping = entityFactory.newEntity(Mapping.class);
+                        mapping.setProvision(provision);
+                        provision.setMapping(mapping);
+                    } else {
+                        mapping.getItems().clear();
+                    }
                     populateMapping(provisionTO.getMapping(), mapping, entityFactory.newEntity(MappingItem.class));
                 }
             }
         }
 
+        // 2. remove all abouts not contained in the TO
+        CollectionUtils.filter(resource.getProvisions(), new Predicate<Provision>() {
+
+            @Override
+            public boolean evaluate(final Provision provision) {
+                return resourceTO.getProvision(provision.getAnyType().getKey()) != null;
+            }
+        });
+
         resource.setCreateTraceLevel(resourceTO.getCreateTraceLevel());
         resource.setUpdateTraceLevel(resourceTO.getUpdateTraceLevel());
         resource.setDeleteTraceLevel(resourceTO.getDeleteTraceLevel());
@@ -207,8 +224,7 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
             }
         }
 
-        // Throw composite exception if there is at least one element set
-        // in the composing exceptions
+        // Throw composite exception if there is at least one element set in the composing exceptions
         if (!requiredValuesMissing.isEmpty()) {
             scce.addException(requiredValuesMissing);
         }
@@ -234,9 +250,9 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
 
     @Override
     public ConnInstance getConnInstance(final ResourceTO resourceTO) {
-        ConnInstance connInstance = connInstanceDAO.find(resourceTO.getConnectorId());
+        ConnInstance connInstance = connInstanceDAO.find(resourceTO.getConnector());
         if (connInstance == null) {
-            throw new NotFoundException("Connector '" + resourceTO.getConnectorId() + "'");
+            throw new NotFoundException("Connector '" + resourceTO.getConnector() + "'");
         }
 
         final ConnInstance connInstanceClone = SerializationUtils.clone(connInstance);
@@ -279,7 +295,7 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
         // set the connector instance
         ConnInstance connector = resource.getConnector();
 
-        resourceTO.setConnectorId(connector == null ? null : connector.getKey());
+        resourceTO.setConnector(connector == null ? null : connector.getKey());
         resourceTO.setConnectorDisplayName(connector == null ? null : connector.getDisplayName());
 
         // set the provision information
@@ -295,6 +311,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
                 provisionTO.setMapping(mappingTO);
                 populateMappingTO(provision.getMapping(), mappingTO);
             }
+
+            resourceTO.getProvisions().add(provisionTO);
         }
 
         resourceTO.setEnforceMandatoryCondition(resource.isEnforceMandatoryCondition());

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
index b286043..124bb7f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RoleDataBinderImpl.java
@@ -84,7 +84,7 @@ public class RoleDataBinderImpl implements RoleDataBinder {
         for (String realmFullPath : roleTO.getRealms()) {
             Realm realm = realmDAO.find(realmFullPath);
             if (realm == null) {
-                LOG.warn("Invalid realm full path {}, ignoring", realmFullPath);
+                LOG.debug("Invalid realm full path {}, ignoring", realmFullPath);
             } else {
                 role.addRealm(realm);
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
index e44198b..f20a265 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
@@ -70,7 +70,7 @@ public class SchemaDataBinderImpl implements SchemaDataBinder {
         boolean hasAttrs = false;
         for (AnyTypeKind anyTypeKind : AnyTypeKind.values()) {
             AnyUtils anyUtils = anyUtilsFactory.getInstance(anyTypeKind);
-            hasAttrs &= schemaDAO.findAttrs(schema, anyUtils.plainAttrClass()).isEmpty();
+            hasAttrs |= schemaDAO.findAttrs(schema, anyUtils.plainAttrClass()).isEmpty();
         }
 
         if (hasAttrs) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
index c25b3ed..762cca5 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
@@ -19,6 +19,8 @@
 package org.apache.syncope.core.provisioning.java.data;
 
 import java.util.Map;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -72,13 +74,10 @@ import org.springframework.stereotype.Component;
 @Component
 public class TaskDataBinderImpl implements TaskDataBinder {
 
-    /**
-     * Logger.
-     */
     private static final Logger LOG = LoggerFactory.getLogger(TaskDataBinder.class);
 
     private static final String[] IGNORE_TASK_PROPERTIES = {
-        "destinationRealm", "executions", "resource", "matchingRule", "unmatchingRule" };
+        "destinationRealm", "templates", "filters", "executions", "resource", "matchingRule", "unmatchingRule" };
 
     private static final String[] IGNORE_TASK_EXECUTION_PROPERTIES = { "key", "task" };
 
@@ -100,40 +99,82 @@ public class TaskDataBinderImpl implements TaskDataBinder {
     @Autowired
     private SchedulerFactoryBean scheduler;
 
-    private void checkJexl(final AnyTO anyTO, final SyncopeClientException sce) {
-        for (AttrTO attrTO : anyTO.getPlainAttrs()) {
-            if (!attrTO.getValues().isEmpty() && !JexlUtils.isExpressionValid(attrTO.getValues().get(0))) {
-                sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0));
+    private void checkTemplateJEXL(final SyncTaskTO syncTaskTO) {
+        SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSyncTask);
+
+        for (Map.Entry<String, AnyTO> entry : syncTaskTO.getTemplates().entrySet()) {
+            for (AttrTO attrTO : entry.getValue().getPlainAttrs()) {
+                if (!attrTO.getValues().isEmpty() && !JexlUtils.isExpressionValid(attrTO.getValues().get(0))) {
+                    sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0));
+                }
+            }
+
+            for (AttrTO attrTO : entry.getValue().getVirAttrs()) {
+                if (!attrTO.getValues().isEmpty() && !JexlUtils.isExpressionValid(attrTO.getValues().get(0))) {
+                    sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0));
+                }
             }
-        }
 
-        for (AttrTO attrTO : anyTO.getVirAttrs()) {
-            if (!attrTO.getValues().isEmpty() && !JexlUtils.isExpressionValid(attrTO.getValues().get(0))) {
-                sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0));
+            if (entry.getValue() instanceof UserTO) {
+                UserTO template = (UserTO) entry.getValue();
+                if (StringUtils.isNotBlank(template.getUsername())
+                        && !JexlUtils.isExpressionValid(template.getUsername())) {
+
+                    sce.getElements().add("Invalid JEXL: " + template.getUsername());
+                }
+                if (StringUtils.isNotBlank(template.getPassword())
+                        && !JexlUtils.isExpressionValid(template.getPassword())) {
+
+                    sce.getElements().add("Invalid JEXL: " + template.getPassword());
+                }
+            } else if (entry.getValue() instanceof GroupTO) {
+                GroupTO template = (GroupTO) entry.getValue();
+                if (StringUtils.isNotBlank(template.getName())
+                        && !JexlUtils.isExpressionValid(template.getName())) {
+
+                    sce.getElements().add("Invalid JEXL: " + template.getName());
+                }
             }
         }
+
+        if (!sce.isEmpty()) {
+            throw sce;
+        }
     }
 
     private void fill(final ProvisioningTask task, final AbstractProvisioningTaskTO taskTO) {
         if (task instanceof PushTask && taskTO instanceof PushTaskTO) {
-            PushTask pushTask = (PushTask) task;
-            PushTaskTO pushTaskTO = (PushTaskTO) taskTO;
-
-            for (Map.Entry<String, String> entry : pushTaskTO.getFilters().entrySet()) {
-                AnyFilter filter = entityFactory.newEntity(AnyFilter.class);
-                filter.setAnyType(anyTypeDAO.find(entry.getKey()));
-                filter.set(entry.getValue());
-
-                filter.setPushTask(pushTask);
-                pushTask.add(filter);
-            }
+            final PushTask pushTask = (PushTask) task;
+            final PushTaskTO pushTaskTO = (PushTaskTO) taskTO;
 
             pushTask.setMatchingRule(pushTaskTO.getMatchingRule() == null
                     ? MatchingRule.LINK : pushTaskTO.getMatchingRule());
-
             pushTask.setUnmatchingRule(pushTaskTO.getUnmatchingRule() == null
                     ? UnmatchingRule.ASSIGN : pushTaskTO.getUnmatchingRule());
 
+            for (Map.Entry<String, String> entry : pushTaskTO.getFilters().entrySet()) {
+                AnyType type = anyTypeDAO.find(entry.getKey());
+                if (type == null) {
+                    LOG.debug("Invalid AnyType {} specified, ignoring...", entry.getKey());
+                } else {
+                    AnyFilter filter = pushTask.getFilter(type);
+                    if (filter == null) {
+                        filter = entityFactory.newEntity(AnyFilter.class);
+                        filter.setAnyType(anyTypeDAO.find(entry.getKey()));
+                        filter.setPushTask(pushTask);
+                        pushTask.add(filter);
+                    }
+                    filter.set(entry.getValue());
+                }
+            }
+            // remove all filters not contained in the TO
+            CollectionUtils.filter(pushTask.getFilters(), new Predicate<AnyFilter>() {
+
+                @Override
+                public boolean evaluate(final AnyFilter anyFilter) {
+                    return pushTaskTO.getFilters().containsKey(anyFilter.getAnyType().getKey());
+                }
+            });
         } else if (task instanceof SyncTask && taskTO instanceof SyncTaskTO) {
             final SyncTask syncTask = (SyncTask) task;
             final SyncTaskTO syncTaskTO = (SyncTaskTO) taskTO;
@@ -142,52 +183,35 @@ public class TaskDataBinderImpl implements TaskDataBinder {
 
             syncTask.setMatchingRule(syncTaskTO.getMatchingRule() == null
                     ? MatchingRule.UPDATE : syncTaskTO.getMatchingRule());
-
             syncTask.setUnmatchingRule(syncTaskTO.getUnmatchingRule() == null
                     ? UnmatchingRule.PROVISION : syncTaskTO.getUnmatchingRule());
 
-            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSyncTask);
-            // 1. validate JEXL expressions in user and group templates
+            // validate JEXL expressions from templates and proceed if fine
+            checkTemplateJEXL(syncTaskTO);
             for (Map.Entry<String, AnyTO> entry : syncTaskTO.getTemplates().entrySet()) {
-                checkJexl(entry.getValue(), sce);
-
-                if (entry.getValue() instanceof UserTO) {
-                    UserTO template = (UserTO) entry.getValue();
-                    if (StringUtils.isNotBlank(template.getUsername())
-                            && !JexlUtils.isExpressionValid(template.getUsername())) {
-
-                        sce.getElements().add("Invalid JEXL: " + template.getUsername());
-                    }
-                    if (StringUtils.isNotBlank(template.getPassword())
-                            && !JexlUtils.isExpressionValid(template.getPassword())) {
-
-                        sce.getElements().add("Invalid JEXL: " + template.getPassword());
-                    }
-                } else if (entry.getValue() instanceof GroupTO) {
-                    GroupTO template = (GroupTO) entry.getValue();
-                    if (StringUtils.isNotBlank(template.getName())
-                            && !JexlUtils.isExpressionValid(template.getName())) {
-
-                        sce.getElements().add("Invalid JEXL: " + template.getName());
+                AnyType type = anyTypeDAO.find(entry.getKey());
+                if (type == null) {
+                    LOG.debug("Invalid AnyType {} specified, ignoring...", entry.getKey());
+                } else {
+                    AnyTemplate anyTemplate = syncTask.getTemplate(type);
+                    if (anyTemplate == null) {
+                        anyTemplate = entityFactory.newEntity(AnyTemplate.class);
+                        anyTemplate.setAnyType(type);
+                        anyTemplate.setSyncTask(syncTask);
+
+                        syncTask.add(anyTemplate);
                     }
+                    anyTemplate.set(entry.getValue());
                 }
             }
-            if (!sce.isEmpty()) {
-                throw sce;
-            }
+            // remove all templates not contained in the TO
+            CollectionUtils.filter(syncTask.getTemplates(), new Predicate<AnyTemplate>() {
 
-            // 2. all JEXL expressions are valid: accept user and group templates
-            for (Map.Entry<String, AnyTO> entry : syncTaskTO.getTemplates().entrySet()) {
-                AnyType type = anyTypeDAO.find(entry.getKey());
-                if (type != null) {
-                    AnyTemplate anyTemplate = entityFactory.newEntity(AnyTemplate.class);
-                    anyTemplate.setAnyType(type);
-                    anyTemplate.set(entry.getValue());
-                    anyTemplate.setSyncTask(syncTask);
-
-                    syncTask.add(anyTemplate);
+                @Override
+                public boolean evaluate(final AnyTemplate anyTemplate) {
+                    return syncTaskTO.getTemplates().containsKey(anyTemplate.getAnyType().getKey());
                 }
-            }
+            });
 
             syncTask.setFullReconciliation(syncTaskTO.isFullReconciliation());
         }
@@ -197,18 +221,13 @@ public class TaskDataBinderImpl implements TaskDataBinder {
         task.setPerformUpdate(taskTO.isPerformUpdate());
         task.setPerformDelete(taskTO.isPerformDelete());
         task.setSyncStatus(taskTO.isSyncStatus());
-        task.getActionsClassNames()
-                .clear();
-        task.getActionsClassNames()
-                .addAll(taskTO.getActionsClassNames());
+        task.getActionsClassNames().clear();
+        task.getActionsClassNames().addAll(taskTO.getActionsClassNames());
     }
 
     @Override
-    public SchedTask createSchedTask(final SchedTaskTO taskTO,
-            final TaskUtils taskUtils
-    ) {
-        final Class<? extends AbstractTaskTO> taskTOClass = taskUtils.taskTOClass();
-
+    public SchedTask createSchedTask(final SchedTaskTO taskTO, final TaskUtils taskUtils) {
+        Class<? extends AbstractTaskTO> taskTOClass = taskUtils.taskTOClass();
         if (taskTOClass == null || !taskTOClass.equals(taskTO.getClass())) {
             throw new IllegalArgumentException(
                     String.format("taskUtils is type %s but task is not: %s", taskTOClass, taskTO.getClass()));
@@ -222,7 +241,7 @@ public class TaskDataBinderImpl implements TaskDataBinder {
         if (taskUtils.getType() == TaskType.SCHEDULED) {
             task.setJobClassName(taskTO.getJobClassName());
         } else if (taskTO instanceof AbstractProvisioningTaskTO) {
-            final AbstractProvisioningTaskTO provisioningTaskTO = (AbstractProvisioningTaskTO) taskTO;
+            AbstractProvisioningTaskTO provisioningTaskTO = (AbstractProvisioningTaskTO) taskTO;
 
             ExternalResource resource = resourceDAO.find(provisioningTaskTO.getResource());
             if (resource == null) {
@@ -237,10 +256,7 @@ public class TaskDataBinderImpl implements TaskDataBinder {
     }
 
     @Override
-    public void updateSchedTask(final SchedTask task,
-            final SchedTaskTO taskTO,
-            final TaskUtils taskUtils
-    ) {
+    public void updateSchedTask(final SchedTask task, final SchedTaskTO taskTO, final TaskUtils taskUtils) {
         Class<? extends Task> taskClass = taskUtils.taskClass();
         Class<? extends AbstractTaskTO> taskTOClass = taskUtils.taskTOClass();
 
@@ -268,8 +284,7 @@ public class TaskDataBinderImpl implements TaskDataBinder {
     }
 
     @Override
-    public TaskExecTO getTaskExecTO(final TaskExec execution
-    ) {
+    public TaskExecTO getTaskExecTO(final TaskExec execution) {
         TaskExecTO executionTO = new TaskExecTO();
         BeanUtils.copyProperties(execution, executionTO, IGNORE_TASK_EXECUTION_PROPERTIES);
 
@@ -347,6 +362,10 @@ public class TaskDataBinderImpl implements TaskDataBinder {
                         ? MatchingRule.UPDATE : ((SyncTask) task).getMatchingRule());
                 ((SyncTaskTO) taskTO).setUnmatchingRule(((SyncTask) task).getUnmatchingRule() == null
                         ? UnmatchingRule.PROVISION : ((SyncTask) task).getUnmatchingRule());
+
+                for (AnyTemplate template : ((SyncTask) task).getTemplates()) {
+                    ((SyncTaskTO) taskTO).getTemplates().put(template.getAnyType().getKey(), template.get());
+                }
                 break;
 
             case PUSH:
@@ -362,6 +381,10 @@ public class TaskDataBinderImpl implements TaskDataBinder {
                         ? MatchingRule.LINK : ((PushTask) task).getMatchingRule());
                 ((PushTaskTO) taskTO).setUnmatchingRule(((PushTask) task).getUnmatchingRule() == null
                         ? UnmatchingRule.ASSIGN : ((PushTask) task).getUnmatchingRule());
+
+                for (AnyFilter filter : ((PushTask) task).getFilters()) {
+                    ((PushTaskTO) taskTO).getFilters().put(filter.getAnyType().getKey(), filter.get());
+                }
                 break;
 
             case NOTIFICATION:

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 54d10bc..6c5e0d6 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -32,6 +32,7 @@ import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.mod.UserMod;
 import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
@@ -49,7 +50,9 @@ import org.apache.syncope.core.misc.security.Encryptor;
 import org.apache.syncope.core.misc.spring.BeanUtils;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
 import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.URelationship;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -58,9 +61,9 @@ import org.springframework.transaction.annotation.Transactional;
 @Transactional(rollbackFor = { Throwable.class })
 public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDataBinder {
 
-    private static final String[] IGNORE_USER_PROPERTIES = {
-        "realm", "roles", "memberships", "plainAttrs", "derAttrs", "virAttrs", "resources",
-        "securityQuestion", "securityAnswer"
+    private static final String[] IGNORE_PROPERTIES = {
+        "type", "realm", "auxClasses", "roles", "dynRoles", "relationships", "memberships", "dynGroups",
+        "plainAttrs", "derAttrs", "virAttrs", "resources", "securityQuestion", "securityAnswer"
     };
 
     @Autowired
@@ -144,6 +147,29 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
             }
         }
 
+        // relationships
+        for (RelationshipTO relationshipTO : userTO.getRelationships()) {
+            AnyObject anyObject = anyObjectDAO.find(relationshipTO.getRightKey());
+
+            if (anyObject == null) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
+                }
+            } else {
+                URelationship relationship = null;
+                if (user.getKey() != null) {
+                    relationship = user.getRelationship(anyObject.getKey());
+                }
+                if (relationship == null) {
+                    relationship = entityFactory.newEntity(URelationship.class);
+                    relationship.setRightEnd(anyObject);
+                    relationship.setLeftEnd(user);
+
+                    user.add(relationship);
+                }
+            }
+        }
+
         // memberships
         for (MembershipTO membershipTO : userTO.getMemberships()) {
             Group group = groupDAO.find(membershipTO.getRightKey());
@@ -266,11 +292,44 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
         // attributes, derived attributes, virtual attributes and resources
         propByRes.merge(fill(user, userMod, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce));
 
-        // store the group ids of membership required to be added
-        Set<Long> membershipToBeAddedGroupKeys = new HashSet<>(userMod.getMembershipsToAdd());
+        Set<String> toBeDeprovisioned = new HashSet<>();
+        Set<String> toBeProvisioned = new HashSet<>();
+
+        // relationships to be removed
+        for (Long anyObjectKey : userMod.getRelationshipsToRemove()) {
+            LOG.debug("Relationship to be removed for any object {}", anyObjectKey);
+
+            URelationship relationship = user.getRelationship(anyObjectKey);
+            if (relationship == null) {
+                LOG.warn("Invalid anyObject key specified for relationship to be removed: {}", anyObjectKey);
+            } else {
+                if (!userMod.getRelationshipsToAdd().contains(anyObjectKey)) {
+                    user.remove(relationship);
+                    toBeDeprovisioned.addAll(relationship.getRightEnd().getResourceNames());
+                }
+            }
+        }
+
+        // relationships to be added
+        for (Long anyObjectKey : userMod.getRelationshipsToAdd()) {
+            LOG.debug("Relationship to be added for any object {}", anyObjectKey);
+
+            AnyObject otherEnd = anyObjectDAO.find(anyObjectKey);
+            if (otherEnd == null) {
+                LOG.debug("Ignoring invalid any object {}", anyObjectKey);
+            } else {
+                URelationship relationship = user.getRelationship(otherEnd.getKey());
+                if (relationship == null) {
+                    relationship = entityFactory.newEntity(URelationship.class);
+                    relationship.setRightEnd(otherEnd);
+                    relationship.setLeftEnd(user);
+
+                    user.add(relationship);
 
-        final Set<String> toBeDeprovisioned = new HashSet<>();
-        final Set<String> toBeProvisioned = new HashSet<>();
+                    toBeProvisioned.addAll(otherEnd.getResourceNames());
+                }
+            }
+        }
 
         // memberships to be removed
         for (Long groupKey : userMod.getMembershipsToRemove()) {
@@ -278,11 +337,10 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
 
             UMembership membership = user.getMembership(groupKey);
             if (membership == null) {
-                LOG.warn("Invalid group key specified for membership to be removed: {}", groupKey);
+                LOG.debug("Invalid group key specified for membership to be removed: {}", groupKey);
             } else {
-                if (membershipToBeAddedGroupKeys.contains(membership.getRightEnd().getKey())) {
+                if (!userMod.getMembershipsToAdd().contains(groupKey)) {
                     user.remove(membership);
-                } else {
                     toBeDeprovisioned.addAll(membership.getRightEnd().getResourceNames());
                 }
             }
@@ -312,10 +370,8 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
         propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned);
         propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned);
 
-        /**
-         * In case of new memberships all the current resources have to be updated in order to propagate new group and
-         * membership attribute values.
-         */
+        // In case of new memberships all current resources need to be updated in order to propagate new group
+        // attribute values.
         if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) {
             currentResources.removeAll(toBeDeprovisioned);
             propByRes.addAll(ResourceOperation.UPDATE, currentResources);
@@ -340,25 +396,43 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
     public UserTO getUserTO(final User user) {
         UserTO userTO = new UserTO();
 
-        BeanUtils.copyProperties(user, userTO, IGNORE_USER_PROPERTIES);
+        BeanUtils.copyProperties(user, userTO, IGNORE_PROPERTIES);
 
         if (user.getSecurityQuestion() != null) {
             userTO.setSecurityQuestion(user.getSecurityQuestion().getKey());
         }
 
-        connObjectUtils.retrieveVirAttrValues(user);
-        fillTO(userTO, user.getRealm().getFullPath(),
+        virAttrHander.retrieveVirAttrValues(user);
+        fillTO(userTO, user.getRealm().getFullPath(), user.getAuxClasses(),
                 user.getPlainAttrs(), user.getDerAttrs(), user.getVirAttrs(), userDAO.findAllResources(user));
 
-        for (UMembership membership : user.getMemberships()) {
-            MembershipTO membershipTO = new MembershipTO();
+        // roles
+        CollectionUtils.collect(user.getRoles(), new Transformer<Role, Long>() {
+
+            @Override
+            public Long transform(final Role role) {
+                return role.getKey();
+            }
+        }, userTO.getRoles());
+
+        // relationships
+        CollectionUtils.collect(user.getRelationships(), new Transformer<URelationship, RelationshipTO>() {
 
-            membershipTO.setKey(membership.getKey());
-            membershipTO.setRightKey(membership.getRightEnd().getKey());
-            membershipTO.setGroupName(membership.getRightEnd().getName());
+            @Override
+            public RelationshipTO transform(final URelationship relationship) {
+                return UserDataBinderImpl.this.getRelationshipTO(relationship);
+            }
 
-            userTO.getMemberships().add(membershipTO);
-        }
+        }, userTO.getRelationships());
+
+        // memberships
+        CollectionUtils.collect(user.getMemberships(), new Transformer<UMembership, MembershipTO>() {
+
+            @Override
+            public MembershipTO transform(final UMembership membership) {
+                return UserDataBinderImpl.this.getMembershipTO(membership);
+            }
+        }, userTO.getMemberships());
 
         // dynamic memberships
         CollectionUtils.collect(userDAO.findDynRoleMemberships(user), new Transformer<Role, Long>() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/dd88efbd/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
index a0469a2..cdc540c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
@@ -54,14 +54,13 @@ import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
-import org.apache.syncope.core.misc.ConnObjectUtils;
 import org.apache.syncope.core.misc.search.SearchCondConverter;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyAbout;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
-import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
+import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.VelocityEngine;
 import org.apache.velocity.context.Context;
@@ -143,7 +142,7 @@ public class NotificationManagerImpl implements NotificationManager {
     private ToolManager velocityToolManager;
 
     @Autowired
-    private ConnObjectUtils connObjectUtils;
+    private VirAttrHandler virAttrHander;
 
     @Autowired
     private UserDataBinder userDataBinder;
@@ -154,9 +153,6 @@ public class NotificationManagerImpl implements NotificationManager {
     @Autowired
     private EntityFactory entityFactory;
 
-    @Autowired
-    private AnyUtilsFactory anyUtilsFactory;
-
     @Transactional(readOnly = true)
     @Override
     public long getMaxRetries() {
@@ -177,10 +173,10 @@ public class NotificationManagerImpl implements NotificationManager {
             final Map<String, Object> model) {
 
         if (any != null) {
-            connObjectUtils.retrieveVirAttrValues(any);
+            virAttrHander.retrieveVirAttrValues(any);
         }
 
-        final List<User> recipients = new ArrayList<>();
+        List<User> recipients = new ArrayList<>();
 
         if (notification.getRecipients() != null) {
             recipients.addAll(searchDAO.<User>search(SyncopeConstants.FULL_ADMIN_REALMS,
@@ -192,10 +188,10 @@ public class NotificationManagerImpl implements NotificationManager {
             recipients.add((User) any);
         }
 
-        final Set<String> recipientEmails = new HashSet<>();
-        final List<UserTO> recipientTOs = new ArrayList<>(recipients.size());
+        Set<String> recipientEmails = new HashSet<>();
+        List<UserTO> recipientTOs = new ArrayList<>(recipients.size());
         for (User recipient : recipients) {
-            connObjectUtils.retrieveVirAttrValues(recipient);
+            virAttrHander.retrieveVirAttrValues(recipient);
 
             String email = getRecipientEmail(notification.getRecipientAttrType(),
                     notification.getRecipientAttrName(), recipient);