http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
diff --cc 
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 6c5e0d6,0000000..b0fb8b0
mode 100644,000000..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
@@@ -1,468 -1,0 +1,473 @@@
 +/*
 + * 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 java.util.Collection;
 +import java.util.Date;
 +import java.util.HashSet;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import org.apache.commons.collections4.Closure;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +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;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +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;
 +
 +@Component
 +@Transactional(rollbackFor = { Throwable.class })
 +public class UserDataBinderImpl extends AbstractAnyDataBinder implements 
UserDataBinder {
 +
 +    private static final String[] IGNORE_PROPERTIES = {
 +        "type", "realm", "auxClasses", "roles", "dynRoles", "relationships", 
"memberships", "dynGroups",
 +        "plainAttrs", "derAttrs", "virAttrs", "resources", 
"securityQuestion", "securityAnswer"
 +    };
 +
 +    @Autowired
 +    private RoleDAO roleDAO;
 +
 +    @Autowired
 +    private ConfDAO confDAO;
 +
 +    @Autowired
 +    private SecurityQuestionDAO securityQuestionDAO;
 +
 +    @Resource(name = "adminUser")
 +    private String adminUser;
 +
 +    @Resource(name = "anonymousUser")
 +    private String anonymousUser;
 +
 +    private final Encryptor encryptor = Encryptor.getInstance();
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getAuthenticatedUserTO() {
 +        final UserTO authUserTO;
 +
 +        final String authUsername = 
AuthContextUtils.getAuthenticatedUsername();
 +        if (anonymousUser.equals(authUsername)) {
 +            authUserTO = new UserTO();
 +            authUserTO.setKey(-2);
 +            authUserTO.setUsername(anonymousUser);
 +        } else if (adminUser.equals(authUsername)) {
 +            authUserTO = new UserTO();
 +            authUserTO.setKey(-1);
 +            authUserTO.setUsername(adminUser);
 +        } else {
 +            User authUser = userDAO.find(authUsername);
-             authUserTO = getUserTO(authUser);
++            authUserTO = getUserTO(authUser, true);
 +        }
 +
 +        return authUserTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public boolean verifyPassword(final String username, final String 
password) {
 +        return verifyPassword(userDAO.authFind(username), password);
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public boolean verifyPassword(final User user, final String password) {
 +        return encryptor.verify(password, user.getCipherAlgorithm(), 
user.getPassword());
 +    }
 +
 +    private void setPassword(final User user, final String password, final 
SyncopeClientCompositeException scce) {
 +        try {
 +            final String algorithm = confDAO.find(
 +                    "password.cipher.algorithm", 
CipherAlgorithm.AES.name()).getValues().get(0).getStringValue();
 +            CipherAlgorithm predefined = CipherAlgorithm.valueOf(algorithm);
 +            user.setPassword(password, predefined);
 +        } catch (IllegalArgumentException e) {
 +            final SyncopeClientException invalidCiperAlgorithm =
 +                    
SyncopeClientException.build(ClientExceptionType.NotFound);
 +            invalidCiperAlgorithm.getElements().add(e.getMessage());
 +            scce.addException(invalidCiperAlgorithm);
 +
 +            throw scce;
 +        }
 +    }
 +
 +    @Override
 +    public void create(final User user, final UserTO userTO, final boolean 
storePassword) {
 +        SyncopeClientCompositeException scce = 
SyncopeClientException.buildComposite();
 +
 +        // roles
 +        for (Long roleKey : userTO.getRoles()) {
 +            Role role = roleDAO.find(roleKey);
 +            if (role == null) {
 +                LOG.warn("Ignoring unknown role with id {}", roleKey);
 +            } else {
 +                user.add(role);
 +            }
 +        }
 +
 +        // 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());
 +
 +            if (group == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid group " + 
membershipTO.getGroupName());
 +                }
 +            } else {
 +                UMembership membership = null;
 +                if (user.getKey() != null) {
 +                    membership = user.getMembership(group.getKey());
 +                }
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(UMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(user);
 +
 +                    user.add(membership);
 +                }
 +            }
 +        }
 +
 +        // realm, attributes, derived attributes, virtual attributes and 
resources
 +        fill(user, userTO, anyUtilsFactory.getInstance(AnyTypeKind.USER), 
scce);
 +
 +        // set password
 +        if (StringUtils.isBlank(userTO.getPassword()) || !storePassword) {
 +            LOG.debug("Password was not provided or not required to be 
stored");
 +        } else {
 +            setPassword(user, userTO.getPassword(), scce);
 +        }
 +
 +        // set username
 +        user.setUsername(userTO.getUsername());
 +
 +        // security question / answer
 +        if (userTO.getSecurityQuestion() != null) {
 +            SecurityQuestion securityQuestion = 
securityQuestionDAO.find(userTO.getSecurityQuestion());
 +            if (securityQuestion != null) {
 +                user.setSecurityQuestion(securityQuestion);
 +            }
 +        }
 +        user.setSecurityAnswer(userTO.getSecurityAnswer());
 +    }
 +
 +    @Override
 +    public PropagationByResource update(final User toBeUpdated, final UserMod 
userMod) {
 +        // Re-merge any pending change from workflow tasks
 +        final User user = userDAO.save(toBeUpdated);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +
 +        SyncopeClientCompositeException scce = 
SyncopeClientException.buildComposite();
 +
 +        Collection<String> currentResources = 
userDAO.findAllResourceNames(user);
 +
 +        // fetch connObjectKeys before update
 +        Map<String, String> oldConnObjectKeys = getConnObjectKeys(user);
 +
 +        // realm
 +        setRealm(user, userMod);
 +
 +        // password
 +        if (StringUtils.isNotBlank(userMod.getPassword())) {
 +            setPassword(user, userMod.getPassword(), scce);
 +            user.setChangePwdDate(new Date());
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // username
 +        if (userMod.getUsername() != null && 
!userMod.getUsername().equals(user.getUsername())) {
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +
 +            user.setUsername(userMod.getUsername());
 +            
AuthContextUtils.updateAuthenticatedUsername(userMod.getUsername());
 +        }
 +
 +        // security question / answer:
 +        // userMod.getSecurityQuestion() is null => remove user security 
question and answer
 +        // userMod.getSecurityQuestion() == 0 => don't change anything
 +        // userMod.getSecurityQuestion() > 0 => update user security question 
and answer
 +        if (userMod.getSecurityQuestion() == null) {
 +            user.setSecurityQuestion(null);
 +            user.setSecurityAnswer(null);
 +        } else if (userMod.getSecurityQuestion() > 0) {
 +            SecurityQuestion securityQuestion = 
securityQuestionDAO.find(userMod.getSecurityQuestion());
 +            if (securityQuestion != null) {
 +                user.setSecurityQuestion(securityQuestion);
 +                user.setSecurityAnswer(userMod.getSecurityAnswer());
 +            }
 +        }
 +
 +        // roles
 +        CollectionUtils.forAllDo(userMod.getRolesToRemove(), new 
Closure<Long>() {
 +
 +            @Override
 +            public void execute(final Long roleKey) {
 +                Role role = roleDAO.find(roleKey);
 +                if (role == null) {
 +                    LOG.warn("Ignoring unknown role with id {}", roleKey);
 +                } else {
 +                    user.remove(role);
 +                }
 +            }
 +        });
 +        CollectionUtils.forAllDo(userMod.getRolesToAdd(), new Closure<Long>() 
{
 +
 +            @Override
 +            public void execute(final Long roleKey) {
 +                Role role = roleDAO.find(roleKey);
 +                if (role == null) {
 +                    LOG.warn("Ignoring unknown role with id {}", roleKey);
 +                } else {
 +                    user.add(role);
 +                }
 +            }
 +        });
 +
 +        // attributes, derived attributes, virtual attributes and resources
 +        propByRes.merge(fill(user, userMod, 
anyUtilsFactory.getInstance(AnyTypeKind.USER), scce));
 +
 +        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);
 +
 +                    toBeProvisioned.addAll(otherEnd.getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be removed
 +        for (Long groupKey : userMod.getMembershipsToRemove()) {
 +            LOG.debug("Membership to be removed for group {}", groupKey);
 +
 +            UMembership membership = user.getMembership(groupKey);
 +            if (membership == null) {
 +                LOG.debug("Invalid group key specified for membership to be 
removed: {}", groupKey);
 +            } else {
 +                if (!userMod.getMembershipsToAdd().contains(groupKey)) {
 +                    user.remove(membership);
 +                    
toBeDeprovisioned.addAll(membership.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be added
 +        for (Long groupKey : userMod.getMembershipsToAdd()) {
 +            LOG.debug("Membership to be added for group {}", groupKey);
 +
 +            Group group = groupDAO.find(groupKey);
 +            if (group == null) {
 +                LOG.debug("Ignoring invalid group {}", groupKey);
 +            } else {
 +                UMembership membership = user.getMembership(group.getKey());
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(UMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(user);
 +
 +                    user.add(membership);
 +
 +                    toBeProvisioned.addAll(group.getResourceNames());
 +                }
 +            }
 +        }
 +
 +        propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned);
 +        propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned);
 +
 +        // 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);
 +        }
 +
 +        // check if some connObjectKey was changed by the update above
 +        Map<String, String> newcCnnObjectKeys = getConnObjectKeys(user);
 +        for (Map.Entry<String, String> entry : oldConnObjectKeys.entrySet()) {
 +            if (newcCnnObjectKeys.containsKey(entry.getKey())
 +                    && 
!entry.getValue().equals(newcCnnObjectKeys.get(entry.getKey()))) {
 +
 +                propByRes.addOldConnObjectKey(entry.getKey(), 
entry.getValue());
 +                propByRes.add(ResourceOperation.UPDATE, entry.getKey());
 +            }
 +        }
 +
 +        return propByRes;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
-     public UserTO getUserTO(final User user) {
++    public UserTO getUserTO(final User user, final boolean details) {
 +        UserTO userTO = new UserTO();
 +
 +        BeanUtils.copyProperties(user, userTO, IGNORE_PROPERTIES);
 +
 +        if (user.getSecurityQuestion() != null) {
 +            userTO.setSecurityQuestion(user.getSecurityQuestion().getKey());
 +        }
 +
-         virAttrHander.retrieveVirAttrValues(user);
++        if (details) {
++            virAttrHander.retrieveVirAttrValues(user);
++        }
++
 +        fillTO(userTO, user.getRealm().getFullPath(), user.getAuxClasses(),
 +                user.getPlainAttrs(), user.getDerAttrs(), user.getVirAttrs(), 
userDAO.findAllResources(user));
 +
-         // roles
-         CollectionUtils.collect(user.getRoles(), new Transformer<Role, 
Long>() {
++        if (details) {
++            // roles
++            CollectionUtils.collect(user.getRoles(), new Transformer<Role, 
Long>() {
 +
-             @Override
-             public Long transform(final Role role) {
-                 return role.getKey();
-             }
-         }, userTO.getRoles());
++                @Override
++                public Long transform(final Role role) {
++                    return role.getKey();
++                }
++            }, userTO.getRoles());
 +
-         // relationships
-         CollectionUtils.collect(user.getRelationships(), new 
Transformer<URelationship, RelationshipTO>() {
++            // relationships
++            CollectionUtils.collect(user.getRelationships(), new 
Transformer<URelationship, RelationshipTO>() {
 +
-             @Override
-             public RelationshipTO transform(final URelationship relationship) 
{
-                 return 
UserDataBinderImpl.this.getRelationshipTO(relationship);
-             }
++                @Override
++                public RelationshipTO transform(final URelationship 
relationship) {
++                    return 
UserDataBinderImpl.this.getRelationshipTO(relationship);
++                }
 +
-         }, userTO.getRelationships());
++            }, userTO.getRelationships());
 +
-         // memberships
-         CollectionUtils.collect(user.getMemberships(), new 
Transformer<UMembership, MembershipTO>() {
++            // memberships
++            CollectionUtils.collect(user.getMemberships(), new 
Transformer<UMembership, MembershipTO>() {
 +
-             @Override
-             public MembershipTO transform(final UMembership membership) {
-                 return UserDataBinderImpl.this.getMembershipTO(membership);
-             }
-         }, userTO.getMemberships());
++                @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>() {
++            // dynamic memberships
++            CollectionUtils.collect(userDAO.findDynRoleMemberships(user), new 
Transformer<Role, Long>() {
 +
-             @Override
-             public Long transform(final Role role) {
-                 return role.getKey();
-             }
-         }, userTO.getDynRoles());
-         CollectionUtils.collect(userDAO.findDynGroupMemberships(user), new 
Transformer<Group, Long>() {
++                @Override
++                public Long transform(final Role role) {
++                    return role.getKey();
++                }
++            }, userTO.getDynRoles());
++            CollectionUtils.collect(userDAO.findDynGroupMemberships(user), 
new Transformer<Group, Long>() {
 +
-             @Override
-             public Long transform(final Group group) {
-                 return group.getKey();
-             }
-         }, userTO.getDynGroups());
++                @Override
++                public Long transform(final Group group) {
++                    return group.getKey();
++                }
++            }, userTO.getDynGroups());
++        }
 +
 +        return userTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final String username) {
-         return getUserTO(userDAO.authFind(username));
++        return getUserTO(userDAO.authFind(username), true);
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final Long key) {
-         return getUserTO(userDAO.authFind(key));
++        return getUserTO(userDAO.authFind(key), true);
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
----------------------------------------------------------------------
diff --cc 
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/NotificationManagerImpl.java
index cdc540c,0000000..65b4618
mode 100644,000000..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
@@@ -1,413 -1,0 +1,413 @@@
 +/*
 + * 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.notification;
 +
 +import 
org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 +import java.io.StringWriter;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AuditElements;
 +import org.apache.syncope.common.lib.types.AuditElements.Result;
 +import org.apache.syncope.common.lib.types.AuditLoggerName;
 +import org.apache.syncope.common.lib.types.IntMappingType;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 +import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 +import org.apache.syncope.core.persistence.api.entity.Notification;
 +import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
 +import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 +import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
 +import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
 +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.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.provisioning.api.VirAttrHandler;
 +import org.apache.velocity.VelocityContext;
 +import org.apache.velocity.app.VelocityEngine;
 +import org.apache.velocity.context.Context;
 +import org.apache.velocity.exception.VelocityException;
 +import org.apache.velocity.tools.ToolManager;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +@Component
 +@Transactional(rollbackFor = { Throwable.class })
 +public class NotificationManagerImpl implements NotificationManager {
 +
 +    /**
 +     * Logger.
 +     */
 +    private static final Logger LOG = 
LoggerFactory.getLogger(NotificationManager.class);
 +
 +    public static final String MAIL_TEMPLATES = "mailTemplates/";
 +
 +    public static final String MAIL_TEMPLATE_HTML_SUFFIX = ".html.vm";
 +
 +    public static final String MAIL_TEMPLATE_TEXT_SUFFIX = ".txt.vm";
 +
 +    /**
 +     * Notification DAO.
 +     */
 +    @Autowired
 +    private NotificationDAO notificationDAO;
 +
 +    /**
 +     * Configuration DAO.
 +     */
 +    @Autowired
 +    private ConfDAO confDAO;
 +
 +    /**
 +     * AnyObject DAO.
 +     */
 +    @Autowired
 +    private AnyObjectDAO anyObjectDAO;
 +
 +    /**
 +     * User DAO.
 +     */
 +    @Autowired
 +    private UserDAO userDAO;
 +
 +    /**
 +     * Group DAO.
 +     */
 +    @Autowired
 +    private GroupDAO groupDAO;
 +
 +    /**
 +     * Search DAO.
 +     */
 +    @Autowired
 +    private AnySearchDAO searchDAO;
 +
 +    /**
 +     * Task DAO.
 +     */
 +    @Autowired
 +    private TaskDAO taskDAO;
 +
 +    /**
 +     * Velocity template engine.
 +     */
 +    @Autowired
 +    private VelocityEngine velocityEngine;
 +
 +    /**
 +     * Velocity tool manager.
 +     */
 +    @Autowired
 +    private ToolManager velocityToolManager;
 +
 +    @Autowired
 +    private VirAttrHandler virAttrHander;
 +
 +    @Autowired
 +    private UserDataBinder userDataBinder;
 +
 +    @Autowired
 +    private GroupDataBinder groupDataBinder;
 +
 +    @Autowired
 +    private EntityFactory entityFactory;
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public long getMaxRetries() {
 +        return confDAO.find("notification.maxRetries", 
"0").getValues().get(0).getLongValue();
 +    }
 +
 +    /**
 +     * Create a notification task.
 +     *
 +     * @param notification notification to take as model
 +     * @param any the any object this task is about
 +     * @param model Velocity model
 +     * @return notification task, fully populated
 +     */
 +    private NotificationTask getNotificationTask(
 +            final Notification notification,
 +            final Any<?, ?, ?> any,
 +            final Map<String, Object> model) {
 +
 +        if (any != null) {
 +            virAttrHander.retrieveVirAttrValues(any);
 +        }
 +
 +        List<User> recipients = new ArrayList<>();
 +
 +        if (notification.getRecipients() != null) {
 +            
recipients.addAll(searchDAO.<User>search(SyncopeConstants.FULL_ADMIN_REALMS,
 +                    SearchCondConverter.convert(notification.getRecipients()),
 +                    Collections.<OrderByClause>emptyList(), 
AnyTypeKind.USER));
 +        }
 +
 +        if (notification.isSelfAsRecipient() && any instanceof User) {
 +            recipients.add((User) any);
 +        }
 +
 +        Set<String> recipientEmails = new HashSet<>();
 +        List<UserTO> recipientTOs = new ArrayList<>(recipients.size());
 +        for (User recipient : recipients) {
 +            virAttrHander.retrieveVirAttrValues(recipient);
 +
 +            String email = 
getRecipientEmail(notification.getRecipientAttrType(),
 +                    notification.getRecipientAttrName(), recipient);
 +            if (email == null) {
 +                LOG.warn("{} cannot be notified: {} not found", recipient, 
notification.getRecipientAttrName());
 +            } else {
 +                recipientEmails.add(email);
-                 recipientTOs.add(userDataBinder.getUserTO(recipient));
++                recipientTOs.add(userDataBinder.getUserTO(recipient, true));
 +            }
 +        }
 +
 +        if (notification.getStaticRecipients() != null) {
 +            recipientEmails.addAll(notification.getStaticRecipients());
 +        }
 +
 +        model.put("recipients", recipientTOs);
 +        model.put("syncopeConf", this.findAllSyncopeConfs());
 +        model.put("events", notification.getEvents());
 +
 +        NotificationTask task = 
entityFactory.newEntity(NotificationTask.class);
 +        task.setTraceLevel(notification.getTraceLevel());
 +        task.getRecipients().addAll(recipientEmails);
 +        task.setSender(notification.getSender());
 +        task.setSubject(notification.getSubject());
 +
 +        String htmlBody = mergeTemplateIntoString(
 +                MAIL_TEMPLATES + notification.getTemplate() + 
MAIL_TEMPLATE_HTML_SUFFIX, model);
 +        String textBody = mergeTemplateIntoString(
 +                MAIL_TEMPLATES + notification.getTemplate() + 
MAIL_TEMPLATE_TEXT_SUFFIX, model);
 +
 +        task.setHtmlBody(htmlBody);
 +        task.setTextBody(textBody);
 +
 +        return task;
 +    }
 +
 +    private String mergeTemplateIntoString(final String templateLocation, 
final Map<String, Object> model) {
 +        StringWriter result = new StringWriter();
 +        try {
 +            Context velocityContext = createVelocityContext(model);
 +            velocityEngine.mergeTemplate(templateLocation, 
SyncopeConstants.DEFAULT_ENCODING, velocityContext, result);
 +        } catch (VelocityException e) {
 +            LOG.error("Could not get mail body", e);
 +        } catch (RuntimeException e) {
 +            // ensure same behaviour as by using Spring 
VelocityEngineUtils.mergeTemplateIntoString()
 +            throw e;
 +        } catch (Exception e) {
 +            LOG.error("Could not get mail body", e);
 +        }
 +
 +        return result.toString();
 +    }
 +
 +    /**
 +     * Create a Velocity Context for the given model, to be passed to the 
template for merging.
 +     *
 +     * @param model Velocity model
 +     * @return Velocity context
 +     */
 +    protected Context createVelocityContext(final Map<String, Object> model) {
 +        Context toolContext = velocityToolManager.createContext();
 +        return new VelocityContext(model, toolContext);
 +    }
 +
 +    @Override
 +    public List<NotificationTask> createTasks(
 +            final AuditElements.EventCategoryType type,
 +            final String category,
 +            final String subcategory,
 +            final String event,
 +            final Result condition,
 +            final Object before,
 +            final Object output,
 +            final Object... input) {
 +
 +        Any<?, ?, ?> any = null;
 +
 +        if (before instanceof UserTO) {
 +            any = userDAO.find(((UserTO) before).getKey());
 +        } else if (output instanceof UserTO) {
 +            any = userDAO.find(((UserTO) output).getKey());
 +        } else if (before instanceof AnyObjectTO) {
 +            any = anyObjectDAO.find(((AnyObjectTO) before).getKey());
 +        } else if (output instanceof AnyObjectTO) {
 +            any = anyObjectDAO.find(((AnyObjectTO) output).getKey());
 +        } else if (before instanceof GroupTO) {
 +            any = groupDAO.find(((GroupTO) before).getKey());
 +        } else if (output instanceof GroupTO) {
 +            any = groupDAO.find(((GroupTO) output).getKey());
 +        }
 +
 +        AnyType anyType = any == null ? null : any.getType();
 +        LOG.debug("Search notification for [{}]{}", anyType, any);
 +
 +        List<NotificationTask> notifications = new ArrayList<>();
 +        for (Notification notification : notificationDAO.findAll()) {
 +            if (LOG.isDebugEnabled()) {
 +                for (AnyAbout about : notification.getAbouts()) {
 +                    LOG.debug("Notification about {} defined: {}", 
about.getAnyType(), about.get());
 +                }
 +            }
 +
 +            if (notification.isActive()) {
 +                String currentEvent = AuditLoggerName.buildEvent(type, 
category, subcategory, event, condition);
 +                if (!notification.getEvents().contains(currentEvent)) {
 +                    LOG.debug("No events found about {}", any);
 +                } else if (anyType == null || any == null
 +                        || notification.getAbout(anyType) == null
 +                        || searchDAO.matches(any,
 +                                
SearchCondConverter.convert(notification.getAbout(anyType).get()), 
anyType.getKind())) {
 +
 +                    LOG.debug("Creating notification task for event {} about 
{}", currentEvent, any);
 +
 +                    final Map<String, Object> model = new HashMap<>();
 +                    model.put("type", type);
 +                    model.put("category", category);
 +                    model.put("subcategory", subcategory);
 +                    model.put("event", event);
 +                    model.put("condition", condition);
 +                    model.put("before", before);
 +                    model.put("output", output);
 +                    model.put("input", input);
 +
 +                    if (any instanceof User) {
-                         model.put("user", userDataBinder.getUserTO((User) 
any));
++                        model.put("user", userDataBinder.getUserTO((User) 
any, true));
 +                    } else if (any instanceof Group) {
-                         model.put("group", groupDataBinder.getGroupTO((Group) 
any));
++                        model.put("group", groupDataBinder.getGroupTO((Group) 
any, true));
 +                    }
 +
 +                    NotificationTask notificationTask = 
getNotificationTask(notification, any, model);
 +                    notificationTask = taskDAO.save(notificationTask);
 +                    notifications.add(notificationTask);
 +                }
 +            } else {
 +                LOG.debug("Notification {} is not active, task will not be 
created", notification.getKey());
 +            }
 +        }
 +        return notifications;
 +    }
 +
 +    private String getRecipientEmail(
 +            final IntMappingType recipientAttrType, final String 
recipientAttrName, final User user) {
 +
 +        String email = null;
 +
 +        switch (recipientAttrType) {
 +            case Username:
 +                email = user.getUsername();
 +                break;
 +
 +            case UserPlainSchema:
 +                UPlainAttr attr = user.getPlainAttr(recipientAttrName);
 +                if (attr != null) {
 +                    email = attr.getValuesAsStrings().isEmpty() ? null : 
attr.getValuesAsStrings().get(0);
 +                }
 +                break;
 +
 +            case UserDerivedSchema:
 +                UDerAttr derAttr = user.getDerAttr(recipientAttrName);
 +                if (derAttr != null) {
 +                    email = derAttr.getValue(user.getPlainAttrs());
 +                }
 +                break;
 +
 +            case UserVirtualSchema:
 +                UVirAttr virAttr = user.getVirAttr(recipientAttrName);
 +                if (virAttr != null) {
 +                    email = virAttr.getValues().isEmpty() ? null : 
virAttr.getValues().get(0);
 +                }
 +                break;
 +
 +            default:
 +        }
 +
 +        return email;
 +    }
 +
 +    @Override
 +    public TaskExec storeExec(final TaskExec execution) {
 +        NotificationTask task = taskDAO.find(execution.getTask().getKey());
 +        task.addExec(execution);
 +        task.setExecuted(true);
 +        taskDAO.save(task);
 +        // this flush call is needed to generate a value for the execution id
 +        taskDAO.flush();
 +        return execution;
 +    }
 +
 +    @Override
 +    public void setTaskExecuted(final Long taskId, final boolean executed) {
 +        NotificationTask task = taskDAO.find(taskId);
 +        task.setExecuted(executed);
 +        taskDAO.save(task);
 +    }
 +
 +    @Override
 +    public long countExecutionsWithStatus(final Long taskId, final String 
status) {
 +        NotificationTask task = taskDAO.find(taskId);
 +        long count = 0;
 +        for (TaskExec taskExec : task.getExecs()) {
 +            if (status == null) {
 +                if (taskExec.getStatus() == null) {
 +                    count++;
 +                }
 +            } else if (status.equals(taskExec.getStatus())) {
 +                count++;
 +            }
 +        }
 +        return count;
 +    }
 +
 +    protected Map<String, String> findAllSyncopeConfs() {
 +        Map<String, String> syncopeConfMap = new HashMap<>();
 +        for (PlainAttr<?> attr : confDAO.get().getPlainAttrs()) {
 +            syncopeConfMap.put(attr.getSchema().getKey(), 
attr.getValuesAsStrings().get(0));
 +        }
 +        return syncopeConfMap;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AnyObjectPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc 
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AnyObjectPushResultHandlerImpl.java
index 503ce0b,0000000..1d00cc3
mode 100644,000000..100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AnyObjectPushResultHandlerImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/AnyObjectPushResultHandlerImpl.java
@@@ -1,157 -1,0 +1,157 @@@
 +/*
 + * 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.sync;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.mod.AnyObjectMod;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +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.persistence.api.entity.Any;
 +import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 +import org.apache.syncope.core.provisioning.api.TimeoutException;
 +import 
org.apache.syncope.core.provisioning.api.sync.AnyObjectPushResultHandler;
 +import org.identityconnectors.framework.common.objects.ConnectorObject;
 +import org.identityconnectors.framework.common.objects.ObjectClass;
 +import org.identityconnectors.framework.common.objects.Uid;
 +
 +public class AnyObjectPushResultHandlerImpl extends AbstractPushResultHandler 
implements AnyObjectPushResultHandler {
 +
 +    @Override
 +    protected AnyUtils getAnyUtils() {
 +        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> deprovision(final Any<?, ?, ?> sbj) {
-         AnyObjectTO before = 
anyObjectDataBinder.getAnyObjectTO(AnyObject.class.cast(sbj));
++        AnyObjectTO before = 
anyObjectDataBinder.getAnyObjectTO(AnyObject.class.cast(sbj), true);
 +
 +        List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        
taskExecutor.execute(propagationManager.getAnyObjectDeleteTasks(before.getKey(),
 noPropResources));
 +
 +        return anyObjectDAO.authFind(before.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> provision(final Any<?, ?, ?> sbj, final Boolean 
enabled) {
-         AnyObjectTO before = 
anyObjectDataBinder.getAnyObjectTO(AnyObject.class.cast(sbj));
++        AnyObjectTO before = 
anyObjectDataBinder.getAnyObjectTO(AnyObject.class.cast(sbj), true);
 +
 +        List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.add(ResourceOperation.CREATE, 
profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getAnyObjectCreateTasks(
 +                before.getKey(),
 +                Collections.unmodifiableCollection(before.getVirAttrs()),
 +                propByRes,
 +                noPropResources));
 +
 +        return anyObjectDAO.authFind(before.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> link(final Any<?, ?, ?> sbj, final Boolean unlink) 
{
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(sbj.getKey());
 +
 +        if (unlink) {
 +            
anyObjectMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        } else {
 +            
anyObjectMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        }
 +
 +        awfAdapter.update(anyObjectMod);
 +
 +        return anyObjectDAO.authFind(sbj.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> unassign(final Any<?, ?, ?> sbj) {
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(sbj.getKey());
 +        
anyObjectMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        awfAdapter.update(anyObjectMod);
 +        return deprovision(sbj);
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> assign(final Any<?, ?, ?> sbj, final Boolean 
enabled) {
 +        AnyObjectMod anyObjectMod = new AnyObjectMod();
 +        anyObjectMod.setKey(sbj.getKey());
 +        
anyObjectMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        awfAdapter.update(anyObjectMod);
 +        return provision(sbj, enabled);
 +    }
 +
 +    @Override
 +    protected String getName(final Any<?, ?, ?> any) {
 +        return StringUtils.EMPTY;
 +    }
 +
 +    @Override
 +    protected AnyTO getAnyTO(final long key) {
 +        try {
 +            return anyObjectDataBinder.getAnyObjectTO(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving user {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> getAny(final long key) {
 +        try {
 +            return anyObjectDAO.authFind(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving anyObject {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected ConnectorObject getRemoteObject(final String connObjectKey, 
final ObjectClass objectClass) {
 +        ConnectorObject obj = null;
 +        try {
 +            Uid uid = new Uid(connObjectKey);
 +
 +            obj = profile.getConnector().getObject(
 +                    objectClass,
 +                    uid,
 +                    
profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
 +        } catch (TimeoutException toe) {
 +            LOG.debug("Request timeout", toe);
 +            throw toe;
 +        } catch (RuntimeException ignore) {
 +            LOG.debug("While resolving {}", connObjectKey, ignore);
 +        }
 +
 +        return obj;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupPushResultHandlerImpl.java
----------------------------------------------------------------------
diff --cc 
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupPushResultHandlerImpl.java
index 78fffbf,0000000..df26d58
mode 100644,000000..100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupPushResultHandlerImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/GroupPushResultHandlerImpl.java
@@@ -1,156 -1,0 +1,156 @@@
 +/*
 + * 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.sync;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import org.apache.syncope.common.lib.mod.GroupMod;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +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.persistence.api.entity.Any;
 +import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 +import org.apache.syncope.core.provisioning.api.TimeoutException;
 +import org.apache.syncope.core.provisioning.api.sync.GroupPushResultHandler;
 +import org.identityconnectors.framework.common.objects.ConnectorObject;
 +import org.identityconnectors.framework.common.objects.ObjectClass;
 +import org.identityconnectors.framework.common.objects.Uid;
 +
 +public class GroupPushResultHandlerImpl extends AbstractPushResultHandler 
implements GroupPushResultHandler {
 +
 +    @Override
 +    protected AnyUtils getAnyUtils() {
 +        return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> deprovision(final Any<?, ?, ?> sbj) {
-         GroupTO before = groupDataBinder.getGroupTO(Group.class.cast(sbj));
++        GroupTO before = groupDataBinder.getGroupTO(Group.class.cast(sbj), 
true);
 +
 +        List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        
taskExecutor.execute(propagationManager.getGroupDeleteTasks(before.getKey(), 
noPropResources));
 +
 +        return groupDAO.authFind(before.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> provision(final Any<?, ?, ?> sbj, final Boolean 
enabled) {
-         GroupTO before = groupDataBinder.getGroupTO(Group.class.cast(sbj));
++        GroupTO before = groupDataBinder.getGroupTO(Group.class.cast(sbj), 
true);
 +
 +        List<String> noPropResources = new ArrayList<>(before.getResources());
 +        noPropResources.remove(profile.getTask().getResource().getKey());
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.add(ResourceOperation.CREATE, 
profile.getTask().getResource().getKey());
 +
 +        taskExecutor.execute(propagationManager.getGroupCreateTasks(
 +                before.getKey(),
 +                Collections.unmodifiableCollection(before.getVirAttrs()),
 +                propByRes,
 +                noPropResources));
 +
 +        return groupDAO.authFind(before.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> link(final Any<?, ?, ?> sbj, final Boolean unlink) 
{
 +        GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(sbj.getKey());
 +
 +        if (unlink) {
 +            
groupMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        } else {
 +            
groupMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        }
 +
 +        gwfAdapter.update(groupMod);
 +
 +        return groupDAO.authFind(sbj.getKey());
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> unassign(final Any<?, ?, ?> sbj) {
 +        GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(sbj.getKey());
 +        
groupMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
 +        gwfAdapter.update(groupMod);
 +        return deprovision(sbj);
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> assign(final Any<?, ?, ?> sbj, final Boolean 
enabled) {
 +        GroupMod groupMod = new GroupMod();
 +        groupMod.setKey(sbj.getKey());
 +        
groupMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
 +        gwfAdapter.update(groupMod);
 +        return provision(sbj, enabled);
 +    }
 +
 +    @Override
 +    protected String getName(final Any<?, ?, ?> any) {
 +        return Group.class.cast(any).getName();
 +    }
 +
 +    @Override
 +    protected AnyTO getAnyTO(final long key) {
 +        try {
 +            return groupDataBinder.getGroupTO(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving user {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected Any<?, ?, ?> getAny(final long key) {
 +        try {
 +            return groupDAO.authFind(key);
 +        } catch (Exception e) {
 +            LOG.warn("Error retrieving group {}", key, e);
 +            return null;
 +        }
 +    }
 +
 +    @Override
 +    protected ConnectorObject getRemoteObject(final String connObjectKey, 
final ObjectClass objectClass) {
 +        ConnectorObject obj = null;
 +        try {
 +            Uid uid = new Uid(connObjectKey);
 +
 +            obj = profile.getConnector().getObject(
 +                    objectClass,
 +                    uid,
 +                    
profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
 +        } catch (TimeoutException toe) {
 +            LOG.debug("Request timeout", toe);
 +            throw toe;
 +        } catch (RuntimeException ignore) {
 +            LOG.debug("While resolving {}", connObjectKey, ignore);
 +        }
 +
 +        return obj;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
----------------------------------------------------------------------
diff --cc 
core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
index 93afe01,0000000..600b6d0
mode 100644,000000..100644
--- 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
+++ 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
@@@ -1,289 -1,0 +1,291 @@@
 +/*
 + * 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.rest.cxf.service;
 +
 +import static 
org.apache.syncope.core.rest.cxf.service.AbstractServiceImpl.LOG;
 +
 +import java.util.List;
 +import javax.ws.rs.core.Response;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.mod.AnyMod;
 +import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.BulkAction;
 +import org.apache.syncope.common.lib.to.BulkActionResult;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.types.ResourceAssociationActionType;
 +import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 +import org.apache.syncope.common.lib.wrap.ResourceKey;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 +import org.apache.syncope.common.rest.api.beans.AnySearchQuery;
 +import org.apache.syncope.common.rest.api.service.AnyService;
 +import org.apache.syncope.core.logic.AbstractAnyLogic;
 +import org.apache.syncope.core.logic.UserLogic;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +
 +public abstract class AbstractAnyService<TO extends AnyTO, MOD extends AnyMod>
 +        extends AbstractServiceImpl
 +        implements AnyService<TO, MOD> {
 +
 +    protected abstract AbstractAnyLogic<TO, MOD> getAnyLogic();
 +
 +    @Override
 +    public TO read(final Long key) {
 +        return getAnyLogic().read(key);
 +    }
 +
 +    @Override
 +    public PagedResult<TO> list(final AnyListQuery listQuery) {
 +        CollectionUtils.transform(listQuery.getRealms(), new 
Transformer<String, String>() {
 +
 +            @Override
 +            public String transform(final String input) {
 +                return StringUtils.prependIfMissing(input, 
SyncopeConstants.ROOT_REALM);
 +            }
 +        });
 +
 +        return buildPagedResult(
 +                getAnyLogic().list(
 +                        listQuery.getPage(),
 +                        listQuery.getSize(),
 +                        getOrderByClauses(listQuery.getOrderBy()),
-                         listQuery.getRealms()),
++                        listQuery.getRealms(),
++                        listQuery.isDetails()),
 +                listQuery.getPage(),
 +                listQuery.getSize(),
 +                getAnyLogic().count(listQuery.getRealms()));
 +    }
 +
 +    @Override
 +    public PagedResult<TO> search(final AnySearchQuery searchQuery) {
 +        CollectionUtils.transform(searchQuery.getRealms(), new 
Transformer<String, String>() {
 +
 +            @Override
 +            public String transform(final String input) {
 +                return StringUtils.prependIfMissing(input, 
SyncopeConstants.ROOT_REALM);
 +            }
 +        });
 +
 +        SearchCond cond = getSearchCond(searchQuery.getFiql());
 +        return buildPagedResult(
 +                getAnyLogic().search(
 +                        cond,
 +                        searchQuery.getPage(),
 +                        searchQuery.getSize(),
 +                        getOrderByClauses(searchQuery.getOrderBy()),
-                         searchQuery.getRealms()),
++                        searchQuery.getRealms(),
++                        searchQuery.isDetails()),
 +                searchQuery.getPage(),
 +                searchQuery.getSize(),
 +                getAnyLogic().searchCount(cond, searchQuery.getRealms()));
 +    }
 +
 +    @Override
 +    public Response create(final TO anyTO) {
 +        TO created = getAnyLogic().create(anyTO);
 +        return createResponse(created.getKey(), created);
 +    }
 +
 +    @Override
 +    public Response update(final MOD anyMod) {
 +        TO any = getAnyLogic().read(anyMod.getKey());
 +
 +        checkETag(any.getETagValue());
 +
 +        TO updated = getAnyLogic().update(anyMod);
 +        return modificationResponse(updated);
 +    }
 +
 +    @Override
 +    public Response delete(final Long key) {
 +        TO group = getAnyLogic().read(key);
 +
 +        checkETag(group.getETagValue());
 +
 +        TO deleted = getAnyLogic().delete(key);
 +        return modificationResponse(deleted);
 +    }
 +
 +    @Override
 +    public Response bulkDeassociation(
 +            final Long key, final ResourceDeassociationActionType type, final 
List<ResourceKey> resourceNames) {
 +
 +        TO any = getAnyLogic().read(key);
 +
 +        checkETag(any.getETagValue());
 +
 +        TO updated;
 +        switch (type) {
 +            case UNLINK:
 +                updated = getAnyLogic().unlink(key, 
CollectionWrapper.unwrap(resourceNames));
 +                break;
 +
 +            case UNASSIGN:
 +                updated = getAnyLogic().unassign(key, 
CollectionWrapper.unwrap(resourceNames));
 +                break;
 +
 +            case DEPROVISION:
 +                updated = getAnyLogic().deprovision(key, 
CollectionWrapper.unwrap(resourceNames));
 +                break;
 +
 +            default:
 +                updated = getAnyLogic().read(key);
 +        }
 +
 +        BulkActionResult result = new BulkActionResult();
 +
 +        if (type == ResourceDeassociationActionType.UNLINK) {
 +            for (ResourceKey resourceName : resourceNames) {
 +                result.getResults().put(resourceName.getElement(),
 +                        
updated.getResources().contains(resourceName.getElement())
 +                                ? BulkActionResult.Status.FAILURE
 +                                : BulkActionResult.Status.SUCCESS);
 +            }
 +        } else {
 +            for (PropagationStatus propagationStatusTO : 
updated.getPropagationStatusTOs()) {
 +                result.getResults().put(propagationStatusTO.getResource(),
 +                        
BulkActionResult.Status.valueOf(propagationStatusTO.getStatus().toString()));
 +            }
 +        }
 +
 +        return modificationResponse(result);
 +    }
 +
 +    @Override
 +    public Response bulkAssociation(
 +            final Long key, final ResourceAssociationActionType type, final 
ResourceAssociationMod associationMod) {
 +
 +        TO any = getAnyLogic().read(key);
 +
 +        checkETag(any.getETagValue());
 +
 +        TO updated;
 +        switch (type) {
 +            case LINK:
 +                updated = getAnyLogic().link(
 +                        key,
 +                        
CollectionWrapper.unwrap(associationMod.getTargetResources()));
 +                break;
 +
 +            case ASSIGN:
 +                updated = getAnyLogic().assign(
 +                        key,
 +                        
CollectionWrapper.unwrap(associationMod.getTargetResources()),
 +                        associationMod.isChangePwd(),
 +                        associationMod.getPassword());
 +                break;
 +
 +            case PROVISION:
 +                updated = getAnyLogic().provision(
 +                        key,
 +                        
CollectionWrapper.unwrap(associationMod.getTargetResources()),
 +                        associationMod.isChangePwd(),
 +                        associationMod.getPassword());
 +                break;
 +
 +            default:
 +                updated = getAnyLogic().read(key);
 +        }
 +
 +        BulkActionResult result = new BulkActionResult();
 +
 +        if (type == ResourceAssociationActionType.LINK) {
 +            for (ResourceKey resourceName : 
associationMod.getTargetResources()) {
 +                result.getResults().put(resourceName.getElement(),
 +                        
updated.getResources().contains(resourceName.getElement())
 +                                ? BulkActionResult.Status.FAILURE
 +                                : BulkActionResult.Status.SUCCESS);
 +            }
 +        } else {
 +            for (PropagationStatus propagationStatusTO : 
updated.getPropagationStatusTOs()) {
 +                result.getResults().put(propagationStatusTO.getResource(),
 +                        
BulkActionResult.Status.valueOf(propagationStatusTO.getStatus().toString()));
 +            }
 +        }
 +
 +        return modificationResponse(result);
 +    }
 +
 +    @Override
 +    public BulkActionResult bulk(final BulkAction bulkAction) {
 +        AbstractAnyLogic<TO, MOD> logic = getAnyLogic();
 +
 +        BulkActionResult result = new BulkActionResult();
 +
 +        switch (bulkAction.getOperation()) {
 +            case DELETE:
 +                for (String key : bulkAction.getTargets()) {
 +                    try {
 +                        result.getResults().put(
 +                                
String.valueOf(logic.delete(Long.valueOf(key)).getKey()),
 +                                BulkActionResult.Status.SUCCESS);
 +                    } catch (Exception e) {
 +                        LOG.error("Error performing delete for user {}", key, 
e);
 +                        result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
 +                    }
 +                }
 +                break;
 +
 +            case SUSPEND:
 +                if (logic instanceof UserLogic) {
 +                    for (String key : bulkAction.getTargets()) {
 +                        StatusMod statusMod = new StatusMod();
 +                        statusMod.setKey(Long.valueOf(key));
 +                        statusMod.setType(StatusMod.ModType.SUSPEND);
 +                        try {
 +                            result.getResults().put(
 +                                    String.valueOf(((UserLogic) 
logic).status(statusMod).getKey()),
 +                                    BulkActionResult.Status.SUCCESS);
 +                        } catch (Exception e) {
 +                            LOG.error("Error performing suspend for user {}", 
key, e);
 +                            result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
 +                        }
 +                    }
 +                }
 +                break;
 +
 +            case REACTIVATE:
 +                for (String key : bulkAction.getTargets()) {
 +                    StatusMod statusMod = new StatusMod();
 +                    statusMod.setKey(Long.valueOf(key));
 +                    statusMod.setType(StatusMod.ModType.REACTIVATE);
 +                    try {
 +                        result.getResults().put(
 +                                String.valueOf(((UserLogic) 
logic).status(statusMod).getKey()),
 +                                BulkActionResult.Status.SUCCESS);
 +                    } catch (Exception e) {
 +                        LOG.error("Error performing reactivate for user {}", 
key, e);
 +                        result.getResults().put(key, 
BulkActionResult.Status.FAILURE);
 +                    }
 +                }
 +                break;
 +
 +            default:
 +        }
 +
 +        return result;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
----------------------------------------------------------------------
diff --cc 
core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
index a594bac,0000000..9265bcb
mode 100644,000000..100644
--- 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
+++ 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
@@@ -1,224 -1,0 +1,221 @@@
 +/*
 + * 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.rest.cxf.service;
 +
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import javax.ws.rs.core.Context;
 +import javax.ws.rs.core.EntityTag;
 +import javax.ws.rs.core.MultivaluedMap;
 +import javax.ws.rs.core.Response;
 +import javax.ws.rs.core.UriBuilder;
 +import javax.ws.rs.core.UriInfo;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.cxf.jaxrs.ext.MessageContext;
 +import org.apache.cxf.jaxrs.ext.search.SearchBean;
 +import org.apache.cxf.jaxrs.ext.search.SearchCondition;
 +import org.apache.cxf.jaxrs.ext.search.SearchContext;
 +import org.apache.syncope.common.lib.AbstractBaseBean;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.rest.api.service.JAXRSService;
 +import org.apache.syncope.common.rest.api.Preference;
 +import org.apache.syncope.common.rest.api.RESTHeaders;
 +import org.apache.syncope.core.misc.search.SearchCondVisitor;
 +import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 +import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +abstract class AbstractServiceImpl implements JAXRSService {
 +
-     /**
-      * Logger.
-      */
 +    protected static final Logger LOG = 
LoggerFactory.getLogger(AbstractServiceImpl.class);
 +
 +    protected static final String OPTIONS_ALLOW = "GET,POST,OPTIONS,HEAD";
 +
 +    @Context
 +    protected UriInfo uriInfo;
 +
 +    @Context
 +    protected MessageContext messageContext;
 +
 +    @Context
 +    protected SearchContext searchContext;
 +
 +    /**
 +     * Reads <tt>Prefer</tt> header from request and parses into a 
<tt>Preference</tt> instance.
 +     *
 +     * @return a <tt>Preference</tt> instance matching the passed 
<tt>Prefer</tt> header,
 +     * or <tt>Preference.NONE</tt> if missing.
 +     */
 +    protected Preference getPreference() {
 +        return 
Preference.fromString(messageContext.getHttpHeaders().getHeaderString(RESTHeaders.PREFER));
 +    }
 +
 +    /**
 +     * Builds response to successful <tt>create</tt> request, taking into 
account any <tt>Prefer</tt> header.
 +     *
 +     * @param id identifier of the created entity
 +     * @param entity the entity just created
 +     * @return response to successful <tt>create</tt> request
 +     */
 +    protected Response createResponse(final Object id, final Object entity) {
 +        Response.ResponseBuilder builder = Response.
 +                
created(uriInfo.getAbsolutePathBuilder().path(String.valueOf(id)).build()).
 +                header(RESTHeaders.RESOURCE_KEY, id);
 +
 +        switch (getPreference()) {
 +            case RETURN_NO_CONTENT:
 +                break;
 +
 +            case RETURN_CONTENT:
 +            case NONE:
 +            default:
 +                builder = builder.entity(entity);
 +                break;
 +
 +        }
 +        if (getPreference() == Preference.RETURN_CONTENT || getPreference() 
== Preference.RETURN_NO_CONTENT) {
 +            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, 
getPreference().toString());
 +        }
 +
 +        return builder.build();
 +    }
 +
 +    /**
 +     * Builds response to successful modification request, taking into 
account any <tt>Prefer</tt> header.
 +     *
 +     * @param entity the entity just modified
 +     * @return response to successful modification request
 +     */
 +    protected Response modificationResponse(final Object entity) {
 +        Response.ResponseBuilder builder;
 +        switch (getPreference()) {
 +            case RETURN_NO_CONTENT:
 +                builder = Response.noContent();
 +                break;
 +
 +            case RETURN_CONTENT:
 +            case NONE:
 +            default:
 +                builder = Response.ok(entity);
 +                break;
 +        }
 +        if (getPreference() == Preference.RETURN_CONTENT || getPreference() 
== Preference.RETURN_NO_CONTENT) {
 +            builder = builder.header(RESTHeaders.PREFERENCE_APPLIED, 
getPreference().toString());
 +        }
 +
 +        return builder.build();
 +    }
 +
 +    protected void checkETag(final String etag) {
 +        Response.ResponseBuilder builder = 
messageContext.getRequest().evaluatePreconditions(new EntityTag(etag));
 +        if (builder != null) {
 +            SyncopeClientException sce = 
SyncopeClientException.build(ClientExceptionType.ConcurrentModification);
 +            sce.getElements().add("Mismatching ETag value");
 +            throw sce;
 +        }
 +    }
 +
 +    protected SearchCond getSearchCond(final String fiql) {
 +        try {
 +            SearchCondVisitor visitor = new SearchCondVisitor();
 +            SearchCondition<SearchBean> sc = searchContext.getCondition(fiql, 
SearchBean.class);
 +            sc.accept(visitor);
 +
 +            return visitor.getQuery();
 +        } catch (Exception e) {
 +            LOG.error("Invalid FIQL expression: {}", fiql, e);
 +
 +            SyncopeClientException sce = 
SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
 +            sce.getElements().add(fiql);
 +            throw sce;
 +        }
 +    }
 +
 +    protected List<OrderByClause> getOrderByClauses(final String orderBy) {
 +        if (StringUtils.isBlank(orderBy)) {
 +            return Collections.<OrderByClause>emptyList();
 +        }
 +
 +        List<OrderByClause> result = new ArrayList<>();
 +
 +        for (String clause : orderBy.split(",")) {
 +            String[] elems = clause.split(" ");
 +
 +            if (elems.length > 0 && StringUtils.isNotBlank(elems[0])) {
 +                OrderByClause obc = new OrderByClause();
 +                obc.setField(elems[0].trim());
 +                if (elems.length > 1 && StringUtils.isNotBlank(elems[1])) {
 +                    
obc.setDirection(elems[1].trim().equalsIgnoreCase(OrderByClause.Direction.ASC.name())
 +                            ? OrderByClause.Direction.ASC : 
OrderByClause.Direction.DESC);
 +                }
 +                result.add(obc);
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    /**
 +     * Builds a paged result out of a list of items and additional 
information.
 +     *
 +     * @param <T> result type
 +     * @param list bare list of items to be returned
 +     * @param page current page
 +     * @param size requested size
 +     * @param totalCount total result size (not considering pagination)
 +     * @return paged result
 +     */
 +    protected <T extends AbstractBaseBean> PagedResult<T> buildPagedResult(
 +            final List<T> list, final int page, final int size, final int 
totalCount) {
 +
 +        PagedResult<T> result = new PagedResult<>();
 +        result.getResult().addAll(list);
 +
 +        result.setPage(page);
 +        result.setSize(result.getResult().size());
 +        result.setTotalCount(totalCount);
 +
 +        UriBuilder builder = uriInfo.getAbsolutePathBuilder();
 +        MultivaluedMap<String, String> queryParams = 
uriInfo.getQueryParameters();
 +        for (Map.Entry<String, List<String>> queryParam : 
queryParams.entrySet()) {
 +            builder = builder.queryParam(queryParam.getKey(), 
queryParam.getValue().toArray());
 +        }
 +
 +        if (result.getPage() > 1) {
 +            result.setPrev(builder.
 +                    replaceQueryParam(PARAM_PAGE, result.getPage() - 1).
 +                    replaceQueryParam(PARAM_SIZE, size).
 +                    build());
 +        }
 +        if ((result.getPage() - 1) * size + result.getSize() < totalCount) {
 +            result.setNext(builder.
 +                    replaceQueryParam(PARAM_PAGE, result.getPage() + 1).
 +                    replaceQueryParam(PARAM_SIZE, size).
 +                    build());
 +        }
 +
 +        return result;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/97607b16/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
----------------------------------------------------------------------
diff --cc 
core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
index ce6390a,0000000..859d008
mode 100644,000000..100644
--- 
a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
+++ 
b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceImpl.java
@@@ -1,71 -1,0 +1,72 @@@
 +/*
 + * 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.rest.cxf.service;
 +
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.mod.AnyObjectMod;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.rest.api.beans.AnyListQuery;
 +import org.apache.syncope.common.rest.api.service.AnyObjectService;
 +import org.apache.syncope.core.logic.AbstractAnyLogic;
 +import org.apache.syncope.core.logic.AnyObjectLogic;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Service;
 +
 +@Service
 +public class AnyObjectServiceImpl extends AbstractAnyService<AnyObjectTO, 
AnyObjectMod> implements AnyObjectService {
 +
 +    @Autowired
 +    private AnyObjectLogic logic;
 +
 +    @Override
 +    protected AbstractAnyLogic<AnyObjectTO, AnyObjectMod> getAnyLogic() {
 +        return logic;
 +    }
 +
 +    @Override
 +    public PagedResult<AnyObjectTO> list(final String type, final 
AnyListQuery listQuery) {
 +        if (StringUtils.isBlank(type)) {
 +            return super.list(listQuery);
 +        }
 +
 +        CollectionUtils.transform(listQuery.getRealms(), new 
Transformer<String, String>() {
 +
 +            @Override
 +            public String transform(final String input) {
 +                return StringUtils.prependIfMissing(input, 
SyncopeConstants.ROOT_REALM);
 +            }
 +        });
 +
 +        return buildPagedResult(
 +                logic.list(
 +                        type,
 +                        listQuery.getPage(),
 +                        listQuery.getSize(),
 +                        getOrderByClauses(listQuery.getOrderBy()),
-                         listQuery.getRealms()),
++                        listQuery.getRealms(),
++                        listQuery.isDetails()),
 +                listQuery.getPage(),
 +                listQuery.getSize(),
 +                getAnyLogic().count(listQuery.getRealms()));
 +    }
 +}

Reply via email to