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 2017/05/08 11:40:16 UTC

[1/8] syncope git commit: White noise

Repository: syncope
Updated Branches:
  refs/heads/2_0_X f8b61e788 -> 07e31980a
  refs/heads/master 2e7e2ceef -> 021230445


White noise


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/a7b31eac
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/a7b31eac
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/a7b31eac

Branch: refs/heads/2_0_X
Commit: a7b31eac66e0093bcb2a444f7b710a1f91602243
Parents: f8b61e7
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue May 2 17:37:55 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon May 8 08:08:32 2017 +0200

----------------------------------------------------------------------
 .../core/provisioning/camel/component/PropagateComponent.java | 1 +
 .../core/provisioning/camel/producer/CreateProducer.java      | 7 +------
 .../core/provisioning/camel/producer/UpdateProducer.java      | 7 +------
 3 files changed, 3 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/a7b31eac/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java
index b8a118a..f443c1d 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java
@@ -58,6 +58,7 @@ public class PropagateComponent extends UriEndpointComponent {
         super(PropagateEndpoint.class);
     }
 
+    @Override
     protected Endpoint createEndpoint(final String uri, final String remaining,
             final Map<String, Object> parameters) throws Exception {
         PropagateType type = PropagateType.valueOf(remaining);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a7b31eac/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
index 54ff11b..8193e3e 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
@@ -70,11 +70,6 @@ public class CreateProducer extends AbstractProducer {
             } else if (actual instanceof AnyTO) {
                 WorkflowResult<String> created = (WorkflowResult<String>) exchange.getIn().getBody();
 
-                AnyTypeKind anyTypeKind = AnyTypeKind.GROUP;
-                if (actual instanceof AnyObjectTO) {
-                    anyTypeKind = AnyTypeKind.ANY_OBJECT;
-                }
-
                 if (actual instanceof GroupTO && isPull()) {
                     Map<String, String> groupOwnerMap = exchange.getProperty("groupOwnerMap", Map.class);
                     AttrTO groupOwner = ((GroupTO) actual).getPlainAttrMap().get(StringUtils.EMPTY);
@@ -93,7 +88,7 @@ public class CreateProducer extends AbstractProducer {
                     exchange.getOut().setBody(new ImmutablePair<>(created.getResult(), null));
                 } else {
                     List<PropagationTask> tasks = getPropagationManager().getCreateTasks(
-                            anyTypeKind,
+                            actual instanceof AnyObjectTO ? AnyTypeKind.ANY_OBJECT : AnyTypeKind.GROUP,
                             created.getResult(),
                             created.getPropByRes(),
                             ((AnyTO) actual).getVirAttrs(),

http://git-wip-us.apache.org/repos/asf/syncope/blob/a7b31eac/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
index 7c3761e..68fbd0e 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
@@ -66,13 +66,8 @@ public class UpdateProducer extends AbstractProducer {
             } else if (actual instanceof AnyPatch) {
                 WorkflowResult<String> updated = (WorkflowResult<String>) exchange.getIn().getBody();
 
-                AnyTypeKind anyTypeKind = AnyTypeKind.GROUP;
-                if (actual instanceof AnyObjectPatch) {
-                    anyTypeKind = AnyTypeKind.ANY_OBJECT;
-                }
-
                 List<PropagationTask> tasks = getPropagationManager().getUpdateTasks(
-                        anyTypeKind,
+                        actual instanceof AnyObjectPatch ? AnyTypeKind.ANY_OBJECT : AnyTypeKind.GROUP,
                         updated.getResult(),
                         false,
                         null,


[7/8] syncope git commit: [SYNCOPE-1077] Extension provided, not enabled by default in fit/core-reference (profile available)

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
index 15a9d3f..54423b8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
@@ -40,6 +40,7 @@ import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 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.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
@@ -59,7 +60,11 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.event.AnyDeletedEvent;
+import org.springframework.aop.support.AopUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -69,9 +74,11 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Autowired
     private PlainAttrDAO plainAttrDAO;
 
+    private UserDAO userDAO;
+
     private AnyObjectDAO anyObjectDAO;
 
-    private UserDAO userDAO;
+    private AnySearchDAO jpaAnySearchDAO;
 
     private UserDAO userDAO() {
         synchronized (this) {
@@ -91,6 +98,20 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
         return anyObjectDAO;
     }
 
+    private AnySearchDAO jpaAnySearchDAO() {
+        synchronized (this) {
+            if (jpaAnySearchDAO == null) {
+                if (AopUtils.getTargetClass(searchDAO()).equals(JPAAnySearchDAO.class)) {
+                    jpaAnySearchDAO = searchDAO();
+                } else {
+                    jpaAnySearchDAO = (AnySearchDAO) ApplicationContextProvider.getBeanFactory().
+                            createBean(JPAAnySearchDAO.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+                }
+            }
+        }
+        return jpaAnySearchDAO;
+    }
+
     @Override
     protected AnyUtils init() {
         return new JPAAnyUtilsFactory().getInstance(AnyTypeKind.GROUP);
@@ -204,8 +225,8 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public List<AMembership> findAMemberships(final Group group) {
         TypedQuery<AMembership> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAAMembership.class.getSimpleName()
-                + " e WHERE e.rightEnd=:group", AMembership.class);
+                "SELECT e FROM " + JPAAMembership.class.getSimpleName() + " e WHERE e.rightEnd=:group",
+                AMembership.class);
         query.setParameter("group", group);
 
         return query.getResultList();
@@ -214,13 +235,23 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public List<UMembership> findUMemberships(final Group group) {
         TypedQuery<UMembership> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAUMembership.class.getSimpleName()
-                + " e WHERE e.rightEnd=:group", UMembership.class);
+                "SELECT e FROM " + JPAUMembership.class.getSimpleName() + " e WHERE e.rightEnd=:group",
+                UMembership.class);
         query.setParameter("group", group);
 
         return query.getResultList();
     }
 
+    @Override
+    public List<Group> findAll(final int page, final int itemsPerPage) {
+        TypedQuery<Group> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAGroup.class.getSimpleName() + " e", Group.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
+
+        return query.getResultList();
+    }
+
     private SearchCond buildDynMembershipCond(final String baseCondFIQL, final Realm groupRealm) {
         AssignableCond cond = new AssignableCond();
         cond.setRealmFullPath(groupRealm.getFullPath());
@@ -232,6 +263,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public Group save(final Group group) {
         Group merged = super.save(group);
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
 
         // refresh dynaminc memberships
         if (merged.getUDynMembership() != null) {
@@ -242,6 +274,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             merged.getUDynMembership().getMembers().clear();
             for (User user : matching) {
                 merged.getUDynMembership().add(user);
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
             }
         }
         for (ADynGroupMembership memb : merged.getADynMemberships()) {
@@ -252,6 +285,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             memb.getMembers().clear();
             for (AnyObject anyObject : matching) {
                 memb.add(anyObject);
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, anyObject));
             }
         }
 
@@ -272,6 +306,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             }
 
             anyObjectDAO().save(leftEnd);
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd));
         }
         for (UMembership membership : findUMemberships(group)) {
             User leftEnd = membership.getLeftEnd();
@@ -285,9 +320,11 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             }
 
             userDAO().save(leftEnd);
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd));
         }
 
         entityManager().remove(group);
+        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.GROUP, group.getKey()));
     }
 
     @Override
@@ -300,35 +337,74 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
         return query.getResultList();
     }
 
+    private List<Group> findWithADynMemberships(final int page, final int itemsPerPage) {
+        TypedQuery<Group> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAGroup.class.getSimpleName() + " e WHERE e.aDynMemberships IS NOT EMPTY",
+                Group.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
+
+        return query.getResultList();
+    }
+
     @Transactional
     @Override
     public void refreshDynMemberships(final AnyObject anyObject) {
-        for (Group group : findAll()) {
-            for (ADynGroupMembership memb : group.getADynMemberships()) {
-                if (searchDAO().matches(
-                        anyObject,
-                        buildDynMembershipCond(memb.getFIQLCond(), group.getRealm()))) {
-
-                    memb.add(anyObject);
-                } else {
-                    memb.getMembers().remove(anyObject);
+        Query countQuery = entityManager().createQuery(
+                "SELECT COUNT(e) FROM  " + JPAGroup.class.getSimpleName() + " e WHERE e.aDynMemberships IS NOT EMPTY");
+        int count = ((Number) countQuery.getSingleResult()).intValue();
+
+        for (int page = 1; page <= (count / DEFAULT_PAGE_SIZE) + 1; page++) {
+            for (Group group : findWithADynMemberships(page, DEFAULT_PAGE_SIZE)) {
+                if (!group.getADynMemberships().isEmpty()) {
+                    for (ADynGroupMembership memb : group.getADynMemberships()) {
+                        if (jpaAnySearchDAO().matches(
+                                anyObject,
+                                buildDynMembershipCond(memb.getFIQLCond(), group.getRealm()))) {
+
+                            memb.add(anyObject);
+                            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+                        } else {
+                            memb.getMembers().remove(anyObject);
+                            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+                        }
+                    }
                 }
             }
         }
+
+    }
+
+    private List<Group> findWithUDynMemberships(final int page, final int itemsPerPage) {
+        TypedQuery<Group> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAGroup.class.getSimpleName() + " e WHERE e.uDynMembership IS NOT NULL",
+                Group.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
+
+        return query.getResultList();
     }
 
     @Transactional
     @Override
     public void refreshDynMemberships(final User user) {
-        for (Group group : findAll()) {
-            if (group.getUDynMembership() != null) {
-                if (searchDAO().matches(
-                        user,
-                        buildDynMembershipCond(group.getUDynMembership().getFIQLCond(), group.getRealm()))) {
-
-                    group.getUDynMembership().add(user);
-                } else {
-                    group.getUDynMembership().getMembers().remove(user);
+        Query countQuery = entityManager().createQuery(
+                "SELECT COUNT(e) FROM  " + JPAGroup.class.getSimpleName() + " e WHERE e.uDynMembership IS NOT NULL");
+        int count = ((Number) countQuery.getSingleResult()).intValue();
+
+        for (int page = 1; page <= (count / DEFAULT_PAGE_SIZE) + 1; page++) {
+            for (Group group : findWithUDynMemberships(page, DEFAULT_PAGE_SIZE)) {
+                if (group.getUDynMembership() != null) {
+                    if (jpaAnySearchDAO().matches(
+                            user,
+                            buildDynMembershipCond(group.getUDynMembership().getFIQLCond(), group.getRealm()))) {
+
+                        group.getUDynMembership().add(user);
+                        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+                    } else {
+                        group.getUDynMembership().getMembers().remove(user);
+                        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+                    }
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
index dbf4f66..a5cabbb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
@@ -31,12 +31,18 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.JPARole;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
 @Repository
 public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
 
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
     private AnySearchDAO searchDAO;
 
     private AnySearchDAO searchDAO() {
@@ -85,6 +91,7 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
             role.getDynMembership().getMembers().clear();
             for (User user : matchingUsers) {
                 role.getDynMembership().add(user);
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
             }
         }
 
@@ -99,6 +106,7 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
 
         for (User user : query.getResultList()) {
             user.getRoles().remove(role);
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
         }
 
         entityManager().remove(role);

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 3dff783..a6b4383 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -76,6 +76,8 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.event.AnyDeletedEvent;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.stereotype.Repository;
@@ -270,6 +272,16 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
         return policies;
     }
 
+    @Override
+    public List<User> findAll(final int page, final int itemsPerPage) {
+        TypedQuery<User> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAUser.class.getSimpleName() + " e", User.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
+
+        return query.getResultList();
+    }
+
     private List<AccountPolicy> getAccountPolicies(final User user) {
         List<AccountPolicy> policies = new ArrayList<>();
 
@@ -430,6 +442,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
             throw e;
         }
 
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
+
         roleDAO.refreshDynMemberships(merged);
         groupDAO().refreshDynMemberships(merged);
 
@@ -451,6 +465,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
         }
 
         entityManager().remove(user);
+        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.USER, user.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
@@ -561,7 +576,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public Collection<String> findAllResourceNames(final String key) {
+    public Collection<String> findAllResourceKeys(final String key) {
         return CollectionUtils.collect(findAllResources(authFind(key)), EntityUtils.keyTransformer());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/resources/persistence.properties
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/persistence.properties b/core/persistence-jpa/src/main/resources/persistence.properties
index 5aa696c..7f2f6f6 100644
--- a/core/persistence-jpa/src/main/resources/persistence.properties
+++ b/core/persistence-jpa/src/main/resources/persistence.properties
@@ -15,3 +15,4 @@
 # specific language governing permissions and limitations
 # under the License.
 content.directory=${conf.directory}
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/resources/persistenceContext.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/persistenceContext.xml b/core/persistence-jpa/src/main/resources/persistenceContext.xml
index 4d12fa1..50153a4 100644
--- a/core/persistence-jpa/src/main/resources/persistenceContext.xml
+++ b/core/persistence-jpa/src/main/resources/persistenceContext.xml
@@ -30,6 +30,7 @@ under the License.
   <context:annotation-config/>
   
   <context:component-scan base-package="org.apache.syncope.core.persistence.jpa"/>
+  <bean class="${any.search.dao}"/>
 
   <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java
index cc324c6..3f4d48a 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java
@@ -47,7 +47,7 @@ public class AnyObjectTest extends AbstractTest {
 
     @Test
     public void findAll() {
-        List<AnyObject> list = anyObjectDAO.findAll();
+        List<AnyObject> list = anyObjectDAO.findAll(1, 100);
         assertFalse(list.isEmpty());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
index 2d7e0ce..c3ef956 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
@@ -47,7 +47,7 @@ public class GroupTest extends AbstractTest {
 
     @Test
     public void findAll() {
-        List<Group> list = groupDAO.findAll();
+        List<Group> list = groupDAO.findAll(1, 100);
         assertEquals("did not get expected number of groups ", 16, list.size());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
index 78dd527..80077a7 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
@@ -95,7 +95,7 @@ public class MultitenancyTest extends AbstractTest {
     @Test
     public void createUser() {
         assertNull(realmDAO.getRoot().getPasswordPolicy());
-        assertTrue(userDAO.findAll().isEmpty());
+        assertTrue(userDAO.findAll(1, 100).isEmpty());
 
         User user = entityFactory.newEntity(User.class);
         user.setRealm(realmDAO.getRoot());

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
index 730a6c2..9988f5a 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
@@ -26,7 +26,6 @@ import static org.junit.Assert.fail;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
@@ -56,7 +55,7 @@ public class UserTest extends AbstractTest {
 
     @Test
     public void findAll() {
-        List<User> list = userDAO.findAll();
+        List<User> list = userDAO.findAll(1, 100);
         assertEquals("did not get expected number of users", 5, list.size());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
index 17c45ba..f91aa7e 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
@@ -22,9 +22,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -60,8 +58,7 @@ public class AnySearchTest extends AbstractTest {
 
     @Test
     public void issueSYNCOPE95() {
-        Set<Group> groups = new HashSet<>(groupDAO.findAll());
-        for (Group group : groups) {
+        for (Group group : groupDAO.findAll(1, 100)) {
             groupDAO.delete(group.getKey());
         }
         groupDAO.flush();

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
index 0f915ea..ede95f7 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
@@ -82,9 +82,6 @@ public class UserTest extends AbstractTest {
     public void delete() {
         List<UMembership> memberships = groupDAO.findUMemberships(groupDAO.findByName("managingDirector"));
         assertFalse(memberships.isEmpty());
-        List<URelationship> relationships = anyObjectDAO.findURelationships(
-                anyObjectDAO.find("fc6dbc3a-6c07-4965-8781-921e7401a4a5"));
-        assertFalse(relationships.isEmpty());
 
         userDAO.delete("c9b2dec2-00a7-4855-97c0-d854842b4b24");
 
@@ -97,9 +94,6 @@ public class UserTest extends AbstractTest {
 
         memberships = groupDAO.findUMemberships(groupDAO.findByName("managingDirector"));
         assertTrue(memberships.isEmpty());
-        relationships = anyObjectDAO.findURelationships(
-                anyObjectDAO.find("fc6dbc3a-6c07-4965-8781-921e7401a4a5"));
-        assertTrue(relationships.isEmpty());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
index 780cd49..ad966de 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
@@ -176,7 +176,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
                 AnyTypeKind.ANY_OBJECT,
                 key,
                 propByRes,
-                CollectionUtils.removeAll(anyObjectDAO.findAllResourceNames(key), resources));
+                CollectionUtils.removeAll(anyObjectDAO.findAllResourceKeys(key), resources));
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
         return propagationReporter.getStatuses();

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index ae1090f..df9881da 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@ -36,13 +36,13 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.StatusPatchType;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
@@ -192,7 +192,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {
 
         PropagationByResource propByRes = new PropagationByResource();
-        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(key));
+        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceKeys(key));
 
         // Note here that we can only notify about "delete", not any other
         // task defined in workflow process definition: this because this
@@ -344,7 +344,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
                 AnyTypeKind.USER,
                 key,
                 propByRes,
-                CollectionUtils.removeAll(userDAO.findAllResourceNames(key), resources));
+                CollectionUtils.removeAll(userDAO.findAllResourceKeys(key), resources));
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
         return propagationReporter.getStatuses();

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
index 1e4ac03..0a8566c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
@@ -34,9 +34,7 @@ public class ExpiredAccessTokenCleanup extends AbstractSchedTaskJobDelegate {
             LOG.debug("Successfully deleted {} expired access tokens", deleted);
         }
 
-        return (dryRun
-                ? "DRY "
-                : "") + "RUNNING";
+        return "SUCCESS";
     }
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
index 36e59a2..8b3f4e5 100755
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
@@ -18,13 +18,10 @@
  */
 package org.apache.syncope.core.provisioning.java.job;
 
-import java.util.Collections;
-import java.util.Date;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 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.conf.CPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.api.entity.user.User;
@@ -36,8 +33,6 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
 
     private static final String RECERTIFICATION_TIME = "identity.recertification.day.interval";
 
-    private static final int PAGE_SIZE = 10;
-
     @Autowired
     private ConfDAO confDAO;
 
@@ -65,27 +60,22 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
         }
     }
 
-    protected boolean isToBeRecertified(final User user) {
-        Date lastCertificationDate = user.getLastRecertification();
+    protected boolean isToBeRecertified(final User user, final long now) {
+        if (!user.isSuspended()
+                && (user.getLastRecertification() == null
+                || user.getLastRecertification().getTime() + recertificationTime < now)) {
 
-        if (lastCertificationDate != null) {
-            if (lastCertificationDate.getTime() + recertificationTime < System.currentTimeMillis()) {
-                LOG.debug("{} is to be recertified", user);
-                return true;
-            } else {
-                LOG.debug("{} do not need to be recertified", user);
-                return false;
-            }
+            LOG.debug("{} is to be recertified", user);
+            return true;
         }
 
-        return true;
+        LOG.debug("{} does not need to be recertified", user);
+        return false;
     }
 
     @Override
     protected String doExecute(final boolean dryRun) throws JobExecutionException {
-        LOG.info("IdentityRecertification {} running [SchedTask {}]", (dryRun
-                ? "dry "
-                : ""), task.getKey());
+        LOG.info("IdentityRecertification {} running [SchedTask {}]", dryRun ? "dry " : "", task.getKey());
 
         init();
         if (recertificationTime == -1) {
@@ -93,13 +83,16 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
             return ("IDENTITY RECERTIFICATION DISABLED");
         }
 
-        for (int page = 1; page <= (userDAO.count() / PAGE_SIZE) + 1; page++) {
-            for (User user : userDAO.findAll(
-                    SyncopeConstants.FULL_ADMIN_REALMS, page, PAGE_SIZE, Collections.<OrderByClause>emptyList())) {
+        if (dryRun) {
+            return "DRY RUN";
+        }
 
+        long now = System.currentTimeMillis();
+        for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            for (User user : userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
                 LOG.debug("Processing user: {}", user.getUsername());
 
-                if (StringUtils.isNotBlank(user.getWorkflowId()) && isToBeRecertified(user) && !dryRun) {
+                if (StringUtils.isNotBlank(user.getWorkflowId()) && isToBeRecertified(user, now)) {
                     uwfAdapter.requestCertify(user);
                 } else {
                     LOG.warn("Workflow for {} is null or empty", user);
@@ -107,9 +100,7 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
             }
         }
 
-        return (dryRun
-                ? "DRY "
-                : "") + "RUNNING";
+        return "SUCCESS";
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 54c53c2..5464276 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -312,7 +312,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                 switch (task.getAnyTypeKind()) {
                     case USER:
                         try {
-                            resources = userDAO.findAllResourceNames(task.getEntityKey());
+                            resources = userDAO.findAllResourceKeys(task.getEntityKey());
                         } catch (Exception e) {
                             LOG.error("Could not read user {}", task.getEntityKey(), e);
                         }
@@ -329,7 +329,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                     case ANY_OBJECT:
                     default:
                         try {
-                            resources = anyObjectDAO.findAllResourceNames(task.getEntityKey());
+                            resources = anyObjectDAO.findAllResourceKeys(task.getEntityKey());
                         } catch (Exception e) {
                             LOG.error("Could not read any object {}", task.getEntityKey(), e);
                         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
index b3035a1..f1ec799 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
@@ -229,7 +229,7 @@ public class PropagationManagerImpl implements PropagationManager {
             origPropByRes.merge(wfResult.getPropByRes());
 
             Set<String> pwdResourceNames = new HashSet<>(userPatch.getPassword().getResources());
-            Collection<String> currentResourceNames = userDAO.findAllResourceNames(userPatch.getKey());
+            Collection<String> currentResourceNames = userDAO.findAllResourceKeys(userPatch.getKey());
             pwdResourceNames.retainAll(currentResourceNames);
             PropagationByResource pwdPropByRes = new PropagationByResource();
             pwdPropByRes.addAll(ResourceOperation.UPDATE, pwdResourceNames);

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
index 8e18ab5..7c3bbe3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
@@ -33,6 +33,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 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.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
@@ -51,8 +52,6 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
 
 public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
 
-    private static final int PAGE_SIZE = 1000;
-
     /**
      * User DAO.
      */
@@ -201,15 +200,21 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
                         ? null
                         : pushTask.getFilter(provision.getAnyType()).getFIQLCond();
                 if (StringUtils.isBlank(filter)) {
-                    doHandle(anyDAO.findAll(), handler, pushTask.getResource());
+                    for (int page = 1; page <= (anyDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                        doHandle(anyDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE), handler, pushTask.getResource());
+                    }
                 } else {
-                    int count = anyDAO.count(SyncopeConstants.FULL_ADMIN_REALMS);
-                    for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
+                    SearchCond cond = SearchCondConverter.convert(filter);
+                    int count = searchDAO.count(
+                            SyncopeConstants.FULL_ADMIN_REALMS,
+                            cond,
+                            provision.getAnyType().getKind());
+                    for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
                         List<? extends Any<?>> anys = searchDAO.search(
                                 SyncopeConstants.FULL_ADMIN_REALMS,
-                                SearchCondConverter.convert(filter),
+                                cond,
                                 page,
-                                PAGE_SIZE,
+                                AnyDAO.DEFAULT_PAGE_SIZE,
                                 Collections.<OrderByClause>emptyList(),
                                 provision.getAnyType().getKind());
                         doHandle(anys, handler, pushTask.getResource());

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
new file mode 100644
index 0000000..c252744
--- /dev/null
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.spring.event;
+
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.springframework.context.ApplicationEvent;
+
+public class AnyCreatedUpdatedEvent<A extends Any<?>> extends ApplicationEvent {
+
+    private static final long serialVersionUID = -781747175059834365L;
+
+    private final A any;
+
+    public AnyCreatedUpdatedEvent(final Object source, final A any) {
+        super(source);
+        this.any = any;
+    }
+
+    public A getAny() {
+        return any;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
new file mode 100644
index 0000000..bd180de
--- /dev/null
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
@@ -0,0 +1,44 @@
+/*
+ * 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.spring.event;
+
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.springframework.context.ApplicationEvent;
+
+public class AnyDeletedEvent extends ApplicationEvent {
+
+    private final AnyTypeKind anyTypeKind;
+
+    private final String anyKey;
+
+    public AnyDeletedEvent(final Object source, final AnyTypeKind anyTypeKind, final String anyKey) {
+        super(source);
+        this.anyTypeKind = anyTypeKind;
+        this.anyKey = anyKey;
+    }
+
+    public AnyTypeKind getAnyTypeKind() {
+        return anyTypeKind;
+    }
+
+    public String getAnyKey() {
+        return anyKey;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-activiti/pom.xml
----------------------------------------------------------------------
diff --git a/core/workflow-activiti/pom.xml b/core/workflow-activiti/pom.xml
index 2ad6c38..753b4e9 100644
--- a/core/workflow-activiti/pom.xml
+++ b/core/workflow-activiti/pom.xml
@@ -74,11 +74,6 @@ under the License.
       <artifactId>syncope-core-workflow-java</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.apache.syncope.core</groupId>
-      <artifactId>syncope-core-spring</artifactId>
-      <version>${project.version}</version>
-    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
index 8fbec72..732d6dd 100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
@@ -216,21 +216,12 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     }
 
     @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
+    protected WorkflowResult<Pair<String, Boolean>> doCreate(
+            final UserTO userTO,
+            final boolean disablePwdPolicyCheck,
+            final Boolean enabled,
             final boolean storePassword) {
 
-        return create(userTO, disablePwdPolicyCheck, null, storePassword);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean storePassword) {
-        return create(userTO, false, storePassword);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
-            final Boolean enabled, final boolean storePassword) {
-
         Map<String, Object> variables = new HashMap<>();
         variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
         variables.put(USER_TO, userTO);

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java
----------------------------------------------------------------------
diff --git a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java
index 46d82a3..992e3d1 100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java
@@ -27,6 +27,7 @@ import org.activiti.engine.identity.GroupQuery;
 import org.activiti.engine.impl.persistence.entity.GroupEntity;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 
 public class SyncopeGroupQueryImpl implements GroupQuery {
@@ -111,15 +112,17 @@ public class SyncopeGroupQueryImpl implements GroupQuery {
             }
         }
         if (result == null) {
-            result = CollectionUtils.collect(groupDAO.findAll(),
-                    new Transformer<org.apache.syncope.core.persistence.api.entity.group.Group, Group>() {
-
-                @Override
-                public Group transform(final org.apache.syncope.core.persistence.api.entity.group.Group user) {
-                    return fromSyncopeGroup(user);
-                }
-
-            }, new ArrayList<Group>());
+            result = new ArrayList<>();
+            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                CollectionUtils.collect(groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE),
+                        new Transformer<org.apache.syncope.core.persistence.api.entity.group.Group, Group>() {
+
+                    @Override
+                    public Group transform(final org.apache.syncope.core.persistence.api.entity.group.Group group) {
+                        return fromSyncopeGroup(group);
+                    }
+                }, result);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java
----------------------------------------------------------------------
diff --git a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java
index dd10566..b7fa2e9 100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java
@@ -27,6 +27,7 @@ import org.activiti.engine.identity.UserQuery;
 import org.activiti.engine.impl.persistence.entity.UserEntity;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -156,15 +157,17 @@ public class SyncopeUserQueryImpl implements UserQuery {
         }
         // THIS CAN BE *VERY* DANGEROUS
         if (result == null) {
-            result = CollectionUtils.collect(userDAO.findAll(),
-                    new Transformer<org.apache.syncope.core.persistence.api.entity.user.User, User>() {
-
-                @Override
-                public User transform(final org.apache.syncope.core.persistence.api.entity.user.User user) {
-                    return fromSyncopeUser(user);
-                }
-
-            }, new ArrayList<User>());
+            result = new ArrayList<>();
+            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                CollectionUtils.collect(userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE),
+                        new Transformer<org.apache.syncope.core.persistence.api.entity.user.User, User>() {
+
+                    @Override
+                    public User transform(final org.apache.syncope.core.persistence.api.entity.user.User user) {
+                        return fromSyncopeUser(user);
+                    }
+                }, result);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-java/pom.xml
----------------------------------------------------------------------
diff --git a/core/workflow-java/pom.xml b/core/workflow-java/pom.xml
index 3d5004b..3b1d75d 100644
--- a/core/workflow-java/pom.xml
+++ b/core/workflow-java/pom.xml
@@ -52,6 +52,11 @@ under the License.
       <artifactId>syncope-core-workflow-api</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-spring</artifactId>
+      <version>${project.version}</version>
+    </dependency>    
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java
index df35de5..232470a 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.workflow.java;
 
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
@@ -48,6 +49,13 @@ public abstract class AbstractAnyObjectWorkflowAdapter
         return null;
     }
 
+    protected abstract WorkflowResult<String> doCreate(AnyObjectTO anyObjectTO);
+
+    @Override
+    public WorkflowResult<String> create(final AnyObjectTO anyObjectTO) {
+        return doCreate(anyObjectTO);
+    }
+
     protected abstract WorkflowResult<String> doUpdate(AnyObject anyObject, AnyObjectPatch anyObjectPatch);
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java
index 7b1fd3e..0689b94 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.workflow.java;
 
 import org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -47,6 +48,13 @@ public abstract class AbstractGroupWorkflowAdapter implements GroupWorkflowAdapt
         return null;
     }
 
+    protected abstract WorkflowResult<String> doCreate(GroupTO groupTO);
+
+    @Override
+    public WorkflowResult<String> create(final GroupTO groupTO) {
+        return doCreate(groupTO);
+    }
+
     protected abstract WorkflowResult<String> doUpdate(Group group, GroupPatch groupPatch);
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
index 48c6d60..d112934 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.workflow.java;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.user.User;
@@ -50,12 +51,12 @@ public abstract class AbstractUserWorkflowAdapter implements UserWorkflowAdapter
     @Autowired
     protected EntityFactory entityFactory;
 
-    public static String encrypt(final String clear) {
+    protected String encrypt(final String clear) {
         byte[] encryptedBytes = EncryptorFactory.getInstance().getDefaultEncryptor().encrypt(clear.getBytes());
         return Base64.encode(encryptedBytes);
     }
 
-    public static String decrypt(final String crypted) {
+    protected String decrypt(final String crypted) {
         byte[] decryptedBytes = EncryptorFactory.getInstance().getDefaultEncryptor().decrypt(Base64.decode(crypted));
         return new String(decryptedBytes);
     }
@@ -65,6 +66,31 @@ public abstract class AbstractUserWorkflowAdapter implements UserWorkflowAdapter
         return null;
     }
 
+    @Override
+    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
+            final boolean storePassword) {
+
+        return create(userTO, disablePwdPolicyCheck, null, storePassword);
+    }
+
+    @Override
+    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean storePassword) {
+        return create(userTO, false, storePassword);
+    }
+
+    protected abstract WorkflowResult<Pair<String, Boolean>> doCreate(
+            UserTO userTO, boolean disablePwdPolicyCheck, Boolean enabled, boolean storePassword);
+
+    @Override
+    public WorkflowResult<Pair<String, Boolean>> create(
+            final UserTO userTO,
+            final boolean disablePwdPolicyCheck,
+            final Boolean enabled,
+            final boolean storePassword) {
+
+        return doCreate(userTO, disablePwdPolicyCheck, enabled, storePassword);
+    }
+
     protected abstract WorkflowResult<String> doActivate(User user, String token);
 
     @Override
@@ -148,7 +174,7 @@ public abstract class AbstractUserWorkflowAdapter implements UserWorkflowAdapter
     protected abstract void doDelete(User user);
 
     @Override
-    public void delete(final String key) {
-        doDelete(userDAO.authFind(key));
+    public void delete(final String userKey) {
+        doDelete(userDAO.authFind(userKey));
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java
index b3a7c48..8852aa0 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java
@@ -38,7 +38,7 @@ import org.apache.syncope.core.workflow.api.WorkflowException;
 public class DefaultAnyObjectWorkflowAdapter extends AbstractAnyObjectWorkflowAdapter {
 
     @Override
-    public WorkflowResult<String> create(final AnyObjectTO anyObjectTO) {
+    protected WorkflowResult<String> doCreate(final AnyObjectTO anyObjectTO) {
         AnyObject anyObject = entityFactory.newEntity(AnyObject.class);
         dataBinder.create(anyObject, anyObjectTO);
         anyObject = anyObjectDAO.save(anyObject);

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java
index a0282dc..f5c9837 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java
@@ -38,7 +38,7 @@ import org.apache.syncope.core.workflow.api.WorkflowException;
 public class DefaultGroupWorkflowAdapter extends AbstractGroupWorkflowAdapter {
 
     @Override
-    public WorkflowResult<String> create(final GroupTO groupTO) {
+    protected WorkflowResult<String> doCreate(final GroupTO groupTO) {
         Group group = entityFactory.newEntity(Group.class);
         dataBinder.create(group, groupTO);
         group = groupDAO.save(group);

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java
index 7b66170..b94930b 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java
@@ -48,21 +48,12 @@ public class DefaultUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     private ConfDAO confDAO;
 
     @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean storePassword) {
-        return create(userTO, false, true);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
+    protected WorkflowResult<Pair<String, Boolean>> doCreate(
+            final UserTO userTO,
+            final boolean disablePwdPolicyCheck,
+            final Boolean enabled,
             final boolean storePassword) {
 
-        return create(userTO, disablePwdPolicyCheck, null, storePassword);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
-            final Boolean enabled, final boolean storePassword) {
-
         User user = entityFactory.newEntity(User.class);
         dataBinder.create(user, userTO, storePassword);
 
@@ -195,7 +186,7 @@ public class DefaultUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
 
     @Override
     public WorkflowResult<String> requestCertify(final User user) {
-        throw new UnsupportedOperationException("Not supported.");
+        throw new WorkflowException(new UnsupportedOperationException("Not supported."));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
index 7ced13d..3578461 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
@@ -63,7 +63,7 @@ public class DeleteProducer extends AbstractProducer {
             switch (getAnyTypeKind()) {
                 case USER:
                     PropagationByResource propByRes = new PropagationByResource();
-                    propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(key));
+                    propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceKeys(key));
                     // Note here that we can only notify about "delete", not any other
                     // task defined in workflow process definition: this because this
                     // information could only be available after uwfAdapter.delete(), which

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
index b5d5d04..5b20d11 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
@@ -71,7 +71,7 @@ public class DeprovisionProducer extends AbstractProducer {
                             AnyTypeKind.USER,
                             key,
                             propByRes,
-                            CollectionUtils.removeAll(userDAO.findAllResourceNames(key), resources));
+                            CollectionUtils.removeAll(userDAO.findAllResourceKeys(key), resources));
                     propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;
@@ -93,7 +93,7 @@ public class DeprovisionProducer extends AbstractProducer {
                             AnyTypeKind.ANY_OBJECT,
                             key,
                             propByRes,
-                            CollectionUtils.removeAll(anyObjectDAO.findAllResourceNames(key), resources));
+                            CollectionUtils.removeAll(anyObjectDAO.findAllResourceKeys(key), resources));
                     propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/client-elasticsearch/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/pom.xml b/ext/elasticsearch/client-elasticsearch/pom.xml
new file mode 100644
index 0000000..48d7f1c
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-elasticsearch</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch Client</name>
+  <description>Apache Syncope Ext: Elasticsearch Client</description>
+  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+  <artifactId>syncope-ext-elasticsearch-client</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-spring</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+      
+    <dependency>
+      <groupId>org.elasticsearch.client</groupId>
+      <artifactId>transport</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
new file mode 100644
index 0000000..e4a30f9
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
@@ -0,0 +1,83 @@
+/*
+ * 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.ext.elasticsearch.client;
+
+import java.net.InetAddress;
+import java.util.Map;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.transport.InetSocketTransportAddress;
+import org.elasticsearch.transport.client.PreBuiltTransportClient;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * Spring {@link FactoryBean} for getting the Elasticsearch's {@link Client} singleton instance.
+ */
+public class ElasticsearchClientFactoryBean implements FactoryBean<Client>, DisposableBean {
+
+    private Map<String, Object> settings;
+
+    private Map<String, Integer> addresses;
+
+    private Client client;
+
+    public void setSettings(final Map<String, Object> settings) {
+        this.settings = settings;
+    }
+
+    public void setAddresses(final Map<String, Integer> addresses) {
+        this.addresses = addresses;
+    }
+
+    @Override
+    public Client getObject() throws Exception {
+        synchronized (this) {
+            if (client == null) {
+                PreBuiltTransportClient tClient = new PreBuiltTransportClient(Settings.builder().put(settings).build());
+
+                for (Map.Entry<String, Integer> entry : addresses.entrySet()) {
+                    tClient.addTransportAddress(
+                            new InetSocketTransportAddress(InetAddress.getByName(entry.getKey()), entry.getValue()));
+                }
+
+                client = tClient;
+            }
+        }
+        return client;
+    }
+
+    @Override
+    public Class<?> getObjectType() {
+        return Client.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        if (client != null) {
+            client.close();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
new file mode 100644
index 0000000..3e20acd
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ext.elasticsearch.client;
+
+import java.io.IOException;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.event.AnyDeletedEvent;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.elasticsearch.action.delete.DeleteResponse;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.action.support.WriteRequest;
+import org.elasticsearch.action.update.UpdateResponse;
+import org.elasticsearch.client.Client;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.event.TransactionalEventListener;
+
+/**
+ * Listen to any create / update and delete in order to keep the Elastsicsearch indexes consistent.
+ */
+public class ElasticsearchIndexManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ElasticsearchIndexManager.class);
+
+    @Autowired
+    private Client client;
+
+    @Autowired
+    private ElasticsearchUtils elasticsearchUtils;
+
+    @TransactionalEventListener
+    public void after(final AnyCreatedUpdatedEvent<Any<?>> event) throws IOException {
+        GetResponse getResponse = client.prepareGet(AuthContextUtils.getDomain().toLowerCase(),
+                event.getAny().getType().getKind().name(),
+                event.getAny().getKey()).
+                get();
+        if (getResponse.isExists()) {
+            LOG.debug("About to update index for {}", event.getAny());
+
+            UpdateResponse response = client.prepareUpdate(
+                    AuthContextUtils.getDomain().toLowerCase(),
+                    event.getAny().getType().getKind().name(),
+                    event.getAny().getKey()).
+                    setDoc(elasticsearchUtils.builder(event.getAny())).
+                    setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).
+                    get();
+            LOG.debug("Index successfully updated for {}: {}", event.getAny(), response);
+        } else {
+            LOG.debug("About to create index for {}", event.getAny());
+
+            IndexResponse response = client.prepareIndex(
+                    AuthContextUtils.getDomain().toLowerCase(),
+                    event.getAny().getType().getKind().name(),
+                    event.getAny().getKey()).
+                    setSource(elasticsearchUtils.builder(event.getAny())).
+                    setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).
+                    get();
+
+            LOG.debug("Index successfully created for {}: {}", event.getAny(), response);
+        }
+    }
+
+    @TransactionalEventListener
+    public void after(final AnyDeletedEvent event) {
+        LOG.debug("About to delete index for {}[{}]", event.getAnyTypeKind(), event.getAnyKey());
+
+        DeleteResponse response = client.prepareDelete(
+                AuthContextUtils.getDomain().toLowerCase(),
+                event.getAnyTypeKind().name(),
+                event.getAnyKey()).
+                setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).
+                get();
+
+        LOG.debug("Index successfully deleted for {}[{}]: {}", event.getAnyTypeKind(), event.getAnyKey(), response);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
new file mode 100644
index 0000000..572ef37
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
@@ -0,0 +1,194 @@
+/*
+ * 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.ext.elasticsearch.client;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
+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.persistence.api.entity.user.UDynGroupMembership;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.URelationship;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Utility methods for usage with Elasticsearch.
+ */
+public class ElasticsearchUtils {
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnyObjectDAO anyObjectDAO;
+
+    private int indexMaxResultWindow = 10000;
+
+    public void setIndexMaxResultWindow(final int indexMaxResultWindow) {
+        this.indexMaxResultWindow = indexMaxResultWindow;
+    }
+
+    public int getIndexMaxResultWindow() {
+        return indexMaxResultWindow;
+    }
+
+    /**
+     * Returns the builder specialized with content from the provided any.
+     *
+     * @param any user, group or any object to index
+     * @return builder specialized with content from the provided any
+     * @throws IOException in case of errors
+     */
+    @Transactional
+    public XContentBuilder builder(final Any<?> any) throws IOException {
+        XContentBuilder builder = XContentFactory.jsonBuilder().
+                startObject().
+                field("id", any.getKey()).
+                field("realm", any.getRealm().getFullPath()).
+                field("anyType", any.getType().getKey()).
+                field("creationDate", any.getCreationDate()).
+                field("creator", any.getCreator()).
+                field("lastChangeDate", any.getLastChangeDate()).
+                field("lastModified", any.getLastModifier()).
+                field("status", any.getStatus()).
+                field("resources",
+                        any instanceof User
+                                ? userDAO.findAllResourceKeys(any.getKey())
+                                : any instanceof AnyObject
+                                        ? anyObjectDAO.findAllResourceKeys(any.getKey())
+                                        : any.getResourceKeys());
+
+        if (any instanceof AnyObject) {
+            AnyObject anyObject = ((AnyObject) any);
+            builder = builder.field("name", anyObject.getName());
+
+            List<Object> memberships = new ArrayList<Object>(anyObjectDAO.findAllGroupKeys(anyObject));
+            builder = builder.field("memberships", memberships);
+
+            List<Object> relationships = new ArrayList<>();
+            List<Object> relationshipTypes = new ArrayList<>();
+            for (ARelationship relationship : anyObjectDAO.findAllARelationships(anyObject)) {
+                relationships.add(relationship.getRightEnd().getKey());
+                relationshipTypes.add(relationship.getType().getKey());
+            }
+            builder = builder.field("relationships", relationships);
+            builder = builder.field("relationshipTypes", relationshipTypes);
+        } else if (any instanceof Group) {
+            Group group = ((Group) any);
+            builder = builder.field("name", group.getName());
+            if (group.getUserOwner() != null) {
+                builder = builder.field("userOwner", group.getUserOwner().getKey());
+            }
+            if (group.getGroupOwner() != null) {
+                builder = builder.field("groupOwner", group.getGroupOwner().getKey());
+            }
+
+            List<Object> members = CollectionUtils.collect(groupDAO.findUMemberships(group),
+                    new Transformer<UMembership, Object>() {
+
+                @Override
+                public Object transform(final UMembership input) {
+                    return input.getLeftEnd().getKey();
+                }
+            }, new ArrayList<>());
+            UDynGroupMembership udynmembership = group.getUDynMembership();
+            if (udynmembership != null) {
+                CollectionUtils.collect(udynmembership.getMembers(), EntityUtils.keyTransformer(), members);
+            }
+            CollectionUtils.collect(groupDAO.findAMemberships(group),
+                    new Transformer<AMembership, Object>() {
+
+                @Override
+                public Object transform(final AMembership input) {
+                    return input.getLeftEnd().getKey();
+                }
+            }, members);
+            for (ADynGroupMembership adynmembership : group.getADynMemberships()) {
+                CollectionUtils.collect(adynmembership.getMembers(), EntityUtils.keyTransformer(), members);
+            }
+            builder = builder.field("members", members);
+        } else if (any instanceof User) {
+            User user = ((User) any);
+            builder = builder.
+                    field("username", user.getUsername()).
+                    field("lastLoginDate", user.getLastLoginDate()).
+                    field("lastRecertification", user.getLastRecertification()).
+                    field("lastRecertificator", user.getLastRecertificator());
+
+            List<Object> roles = CollectionUtils.collect(userDAO.findAllRoles(user),
+                    EntityUtils.<Role>keyTransformer(), new ArrayList<>());
+            builder = builder.field("roles", roles);
+
+            List<Object> memberships = new ArrayList<Object>(userDAO.findAllGroupKeys(user));
+            builder = builder.field("memberships", memberships);
+
+            List<Object> relationships = new ArrayList<>();
+            Set<Object> relationshipTypes = new HashSet<>();
+            for (URelationship relationship : user.getRelationships()) {
+                relationships.add(relationship.getRightEnd().getKey());
+                relationshipTypes.add(relationship.getType().getKey());
+            }
+            builder = builder.field("relationships", relationships);
+            builder = builder.field("relationshipTypes", relationshipTypes);
+        }
+
+        for (PlainAttr<?> plainAttr : any.getPlainAttrs()) {
+            List<Object> values = CollectionUtils.collect(plainAttr.getValues(),
+                    new Transformer<PlainAttrValue, Object>() {
+
+                @Override
+                public Object transform(final PlainAttrValue input) {
+                    return input.getValue();
+                }
+            }, new ArrayList<>(plainAttr.getValues().size()));
+            if (plainAttr.getUniqueValue() != null) {
+                values.add(plainAttr.getUniqueValue().getValue());
+            }
+
+            builder = builder.field(plainAttr.getSchema().getKey(), values);
+        }
+
+        builder = builder.endObject();
+
+        return builder;
+    }
+}


[8/8] syncope git commit: [SYNCOPE-1077] Extension provided, not enabled by default in fit/core-reference (profile available)

Posted by il...@apache.org.
[SYNCOPE-1077] Extension provided, not enabled by default in fit/core-reference (profile available)


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/02123044
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/02123044
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/02123044

Branch: refs/heads/master
Commit: 021230445caf8d026a1d09b10ae4722dadb49984
Parents: 9e09aa1
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon May 8 13:32:27 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon May 8 13:40:00 2017 +0200

----------------------------------------------------------------------
 .../client/console/commons/Constants.java       |   2 +-
 .../client/enduser/resources/InfoResource.java  |   2 -
 .../enduser/resources/SchemaResource.java       |   2 -
 .../resources/UserSelfCreateResource.java       |   2 -
 .../enduser/resources/UserSelfReadResource.java |   2 -
 .../syncope/common/lib/info/PlatformInfo.java   |  10 +
 .../apache/syncope/core/logic/SyncopeLogic.java |   9 +-
 .../init/ClassPathScanImplementationLookup.java |  47 +--
 .../core/logic/report/GroupReportlet.java       |  23 +-
 .../logic/report/ReconciliationReportlet.java   |   9 +-
 .../core/logic/report/UserReportlet.java        |  23 +-
 .../core/persistence/api/dao/AnyDAO.java        |  15 +-
 .../core/persistence/api/dao/AnyObjectDAO.java  |   7 +-
 .../core/persistence/api/dao/GroupDAO.java      |   2 -
 .../core/persistence/api/dao/UserDAO.java       |   4 +-
 core/persistence-jpa/pom.xml                    |   5 -
 .../persistence/jpa/dao/AbstractAnyDAO.java     |  11 +-
 .../jpa/dao/AbstractAnySearchDAO.java           | 343 ++++++++++++++++++
 .../persistence/jpa/dao/JPAAnyObjectDAO.java    |  45 ++-
 .../persistence/jpa/dao/JPAAnySearchDAO.java    | 354 ++++--------------
 .../core/persistence/jpa/dao/JPAGroupDAO.java   | 122 +++++--
 .../core/persistence/jpa/dao/JPARoleDAO.java    |   8 +
 .../core/persistence/jpa/dao/JPAUserDAO.java    |  17 +-
 .../src/main/resources/persistence.properties   |   1 +
 .../src/main/resources/persistenceContext.xml   |   1 +
 .../persistence/jpa/inner/AnyObjectTest.java    |   2 +-
 .../core/persistence/jpa/inner/GroupTest.java   |   2 +-
 .../persistence/jpa/inner/MultitenancyTest.java |   2 +-
 .../core/persistence/jpa/inner/UserTest.java    |   3 +-
 .../persistence/jpa/outer/AnySearchTest.java    |   5 +-
 .../core/persistence/jpa/outer/UserTest.java    |   6 -
 .../DefaultAnyObjectProvisioningManager.java    |   2 +-
 .../java/DefaultUserProvisioningManager.java    |   6 +-
 .../java/job/ExpiredAccessTokenCleanup.java     |   4 +-
 .../java/job/IdentityRecertification.java       |  45 +--
 .../AbstractPropagationTaskExecutor.java        |   4 +-
 .../propagation/PropagationManagerImpl.java     |   2 +-
 .../java/pushpull/PushJobDelegate.java          |  19 +-
 .../spring/event/AnyCreatedUpdatedEvent.java    |  39 ++
 .../core/spring/event/AnyDeletedEvent.java      |  44 +++
 core/workflow-activiti/pom.xml                  |   5 -
 .../activiti/ActivitiUserWorkflowAdapter.java   |  17 +-
 .../activiti/SyncopeGroupQueryImpl.java         |  21 +-
 .../workflow/activiti/SyncopeUserQueryImpl.java |  21 +-
 core/workflow-java/pom.xml                      |   5 +
 .../java/AbstractAnyObjectWorkflowAdapter.java  |   8 +
 .../java/AbstractGroupWorkflowAdapter.java      |   8 +
 .../java/AbstractUserWorkflowAdapter.java       |  34 +-
 .../java/DefaultAnyObjectWorkflowAdapter.java   |   2 +-
 .../java/DefaultGroupWorkflowAdapter.java       |   2 +-
 .../java/DefaultUserWorkflowAdapter.java        |  19 +-
 .../camel/producer/DeleteProducer.java          |   2 +-
 .../camel/producer/DeprovisionProducer.java     |   4 +-
 ext/elasticsearch/client-elasticsearch/pom.xml  |  61 ++++
 .../client/ElasticsearchClientFactoryBean.java  |  83 +++++
 .../client/ElasticsearchIndexManager.java       |  95 +++++
 .../client/ElasticsearchUtils.java              | 194 ++++++++++
 .../resources/elasticsearchClientContext.xml    |  50 +++
 ext/elasticsearch/persistence-jpa/pom.xml       |  62 ++++
 .../jpa/dao/ElasticsearchAnySearchDAO.java      | 355 +++++++++++++++++++
 .../src/main/resources/persistence.properties   |  18 +
 ext/elasticsearch/pom.xml                       |  46 +++
 ext/elasticsearch/provisioning-java/pom.xml     |  62 ++++
 .../java/job/ElasticsearchReindex.java          | 137 +++++++
 ext/pom.xml                                     |   1 +
 fit/core-reference/pom.xml                      |  72 ++++
 .../core/reference/ITImplementationLookup.java  |  40 ++-
 .../elasticsearch/persistence.properties        |  18 +
 .../src/main/resources/elasticsearch/web.xml    |  50 +++
 .../src/main/resources/log4j2.xml               |  10 +-
 fit/core-reference/src/test/resources/rebel.xml |   9 +
 pom.xml                                         |  18 +-
 72 files changed, 2265 insertions(+), 515 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
index 11bdc72..1eedd67 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
@@ -45,7 +45,7 @@ public final class Constants {
 
     public static final String USERNAME_FIELD_NAME = "username";
 
-    public static final String OBJNAME_FIELD_NAME = "name";
+    public static final String NAME_FIELD_NAME = "name";
 
     public static final String DEFAULT_TOKEN_FIELD_NAME = "token";
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
index c96fa2a..23af8fc 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.enduser.resources;
 
-import static org.apache.syncope.client.enduser.resources.BaseResource.MAPPER;
-
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
index 5edd4b0..b088201 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.enduser.resources;
 
-import static org.apache.syncope.client.enduser.resources.BaseResource.MAPPER;
-
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
index b7775b1..8b42285 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.enduser.resources;
 
-import static org.apache.syncope.client.enduser.resources.BaseResource.LOG;
-
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
index 81ffcd1..c4cb6c1 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.enduser.resources;
 
-import static org.apache.syncope.client.enduser.resources.BaseResource.MAPPER;
-
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java b/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java
index 3d21d69..bbe843d 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java
@@ -59,6 +59,8 @@ public class PlatformInfo extends AbstractBaseBean {
 
     private String passwordGenerator;
 
+    private String anySearchDAO;
+
     private final Set<String> entitlements = new HashSet<>();
 
     private final Set<String> reportletConfs = new HashSet<>();
@@ -148,6 +150,14 @@ public class PlatformInfo extends AbstractBaseBean {
         this.passwordGenerator = passwordGenerator;
     }
 
+    public String getAnySearchDAO() {
+        return anySearchDAO;
+    }
+
+    public void setAnySearchDAO(final String anySearchDAO) {
+        this.anySearchDAO = anySearchDAO;
+    }
+
     @XmlElementWrapper(name = "entitlements")
     @XmlElement(name = "entitlement")
     @JsonProperty("entitlements")

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index 0f0dcb4..2c01e66 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -38,6 +38,7 @@ import org.apache.syncope.core.spring.security.PasswordGenerator;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.ImplementationLookup.Type;
 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.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -139,6 +140,9 @@ public class SyncopeLogic extends AbstractLogic<AbstractBaseBean> {
     private PasswordGenerator passwordGenerator;
 
     @Autowired
+    private AnySearchDAO anySearchDAO;
+
+    @Autowired
     private ImplementationLookup implLookup;
 
     public boolean isSelfRegAllowed() {
@@ -173,8 +177,9 @@ public class SyncopeLogic extends AbstractLogic<AbstractBaseBean> {
                 PLATFORM_INFO.setAnyObjectProvisioningManager(AopUtils.getTargetClass(aProvisioningManager).getName());
                 PLATFORM_INFO.setUserProvisioningManager(AopUtils.getTargetClass(uProvisioningManager).getName());
                 PLATFORM_INFO.setGroupProvisioningManager(AopUtils.getTargetClass(gProvisioningManager).getName());
-                PLATFORM_INFO.setVirAttrCache(virAttrCache.getClass().getName());
-                PLATFORM_INFO.setPasswordGenerator(passwordGenerator.getClass().getName());
+                PLATFORM_INFO.setVirAttrCache(AopUtils.getTargetClass(virAttrCache).getName());
+                PLATFORM_INFO.setPasswordGenerator(AopUtils.getTargetClass(passwordGenerator).getName());
+                PLATFORM_INFO.setAnySearchDAO(AopUtils.getTargetClass(anySearchDAO).getName());
 
                 PLATFORM_INFO.getReportletConfs().addAll(implLookup.getClassNames(Type.REPORTLET_CONF));
                 PLATFORM_INFO.getAccountRules().addAll(implLookup.getClassNames(Type.ACCOUNT_RULE_CONF));

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index b91f2aa..3799150 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -28,34 +28,35 @@ import java.util.Set;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.ImplementationLookup.Type;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
 import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import org.apache.syncope.core.provisioning.api.LogicActions;
 import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer;
 import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
 import org.apache.syncope.core.provisioning.api.notification.NotificationRecipientsProvider;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
+import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
+import org.apache.syncope.core.provisioning.api.pushpull.PullCorrelationRule;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
-import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
+import org.apache.syncope.core.provisioning.api.pushpull.ReconciliationFilterBuilder;
+import org.apache.syncope.core.provisioning.java.data.JEXLMappingItemTransformerImpl;
+import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate;
+import org.apache.syncope.core.provisioning.java.pushpull.PlainAttrsPullCorrelationRule;
 import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
+import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
 import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.util.ClassUtils;
-import org.apache.syncope.core.provisioning.api.pushpull.ReconciliationFilterBuilder;
-import org.apache.syncope.core.provisioning.api.pushpull.PullCorrelationRule;
-import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
-import org.apache.syncope.core.provisioning.java.data.JEXLMappingItemTransformerImpl;
-import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate;
-import org.apache.syncope.core.provisioning.java.pushpull.PlainAttrsPullCorrelationRule;
 
 /**
  * Cache class names for all implementations of Syncope interfaces found in classpath, for later usage.
@@ -119,9 +120,9 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
             try {
                 Class<?> clazz = ClassUtils.resolveClassName(
                         bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-                boolean isAbsractClazz = Modifier.isAbstract(clazz.getModifiers());
+                boolean isAbstractClazz = Modifier.isAbstract(clazz.getModifiers());
 
-                if (Reportlet.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (Reportlet.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     ReportletConfClass annotation = clazz.getAnnotation(ReportletConfClass.class);
                     if (annotation == null) {
                         LOG.warn("Found Reportlet {} without declared configuration", clazz.getName());
@@ -131,7 +132,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     }
                 }
 
-                if (AccountRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (AccountRule.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     AccountRuleConfClass annotation = clazz.getAnnotation(AccountRuleConfClass.class);
                     if (annotation == null) {
                         LOG.warn("Found account policy rule {} without declared configuration", clazz.getName());
@@ -141,7 +142,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     }
                 }
 
-                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     PasswordRuleConfClass annotation = clazz.getAnnotation(PasswordRuleConfClass.class);
                     if (annotation == null) {
                         LOG.warn("Found password policy rule {} without declared configuration", clazz.getName());
@@ -151,13 +152,13 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     }
                 }
 
-                if (MappingItemTransformer.class.isAssignableFrom(clazz) && !isAbsractClazz
+                if (MappingItemTransformer.class.isAssignableFrom(clazz) && !isAbstractClazz
                         && !clazz.equals(JEXLMappingItemTransformerImpl.class)) {
 
                     classNames.get(Type.MAPPING_ITEM_TRANSFORMER).add(clazz.getName());
                 }
 
-                if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbsractClazz
+                if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbstractClazz
                         && !PullJobDelegate.class.isAssignableFrom(clazz)
                         && !PushJobDelegate.class.isAssignableFrom(clazz)
                         && !GroupMemberProvisionTaskJobDelegate.class.isAssignableFrom(clazz)) {
@@ -165,36 +166,36 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     classNames.get(Type.TASKJOBDELEGATE).add(bd.getBeanClassName());
                 }
 
-                if (ReconciliationFilterBuilder.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (ReconciliationFilterBuilder.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.RECONCILIATION_FILTER_BUILDER).add(bd.getBeanClassName());
                 }
 
-                if (LogicActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (LogicActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.LOGIC_ACTIONS).add(bd.getBeanClassName());
                 }
 
-                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.PROPAGATION_ACTIONS).add(bd.getBeanClassName());
                 }
 
-                if (PullActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (PullActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.PULL_ACTIONS).add(bd.getBeanClassName());
                 }
 
-                if (PushActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (PushActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.PUSH_ACTIONS).add(bd.getBeanClassName());
                 }
 
-                if (PullCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz
+                if (PullCorrelationRule.class.isAssignableFrom(clazz) && !isAbstractClazz
                         && !PlainAttrsPullCorrelationRule.class.isAssignableFrom(clazz)) {
                     classNames.get(Type.PULL_CORRELATION_RULE).add(bd.getBeanClassName());
                 }
 
-                if (Validator.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (Validator.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.VALIDATOR).add(bd.getBeanClassName());
                 }
 
-                if (NotificationRecipientsProvider.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (NotificationRecipientsProvider.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.NOTIFICATION_RECIPIENTS_PROVIDER).add(bd.getBeanClassName());
                 }
             } catch (Throwable t) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
index 4bc4a37..0aeaf0a 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
@@ -31,6 +31,7 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -47,8 +48,6 @@ import org.xml.sax.helpers.AttributesImpl;
 @ReportletConfClass(GroupReportletConf.class)
 public class GroupReportlet extends AbstractReportlet {
 
-    private static final int PAGE_SIZE = 10;
-
     @Autowired
     private GroupDAO groupDAO;
 
@@ -297,15 +296,21 @@ public class GroupReportlet extends AbstractReportlet {
 
         doExtractConf(handler);
 
-        if (StringUtils.isBlank(this.conf.getMatchingCond())) {
-            doExtract(handler, groupDAO.findAll());
-        } else {
-            for (int page = 1; page <= (count() / PAGE_SIZE) + 1; page++) {
-                List<Group> groups = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
+        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            List<Group> groups;
+            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
+                groups = groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
+            } else {
+                groups = searchDAO.search(
+                        SyncopeConstants.FULL_ADMIN_REALMS,
                         SearchCondConverter.convert(this.conf.getMatchingCond()),
-                        page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), AnyTypeKind.GROUP);
-                doExtract(handler, groups);
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        AnyTypeKind.USER);
             }
+
+            doExtract(handler, groups);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
index ac5c1f1..4922015 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
@@ -35,6 +35,7 @@ import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
 import org.apache.syncope.common.lib.report.ReconciliationReportletConf.Feature;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
 import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
@@ -402,7 +403,9 @@ public class ReconciliationReportlet extends AbstractReportlet {
             atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(userDAO.count()));
             handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
 
-            doExtract(handler, userDAO.findAll());
+            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                doExtract(handler, userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
+            }
         } else {
             SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
 
@@ -419,7 +422,9 @@ public class ReconciliationReportlet extends AbstractReportlet {
             atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(groupDAO.count()));
             handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
 
-            doExtract(handler, groupDAO.findAll());
+            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                doExtract(handler, groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
+            }
         } else {
             SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
index 38fba8a..bb1048b 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
@@ -33,6 +33,7 @@ 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.core.persistence.api.dao.AnyDAO;
 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.user.User;
@@ -53,8 +54,6 @@ import org.xml.sax.helpers.AttributesImpl;
 @ReportletConfClass(UserReportletConf.class)
 public class UserReportlet extends AbstractReportlet {
 
-    private static final int PAGE_SIZE = 10;
-
     @Autowired
     private UserDAO userDAO;
 
@@ -364,15 +363,21 @@ public class UserReportlet extends AbstractReportlet {
 
         doExtractConf(handler);
 
-        if (StringUtils.isBlank(this.conf.getMatchingCond())) {
-            doExtract(handler, userDAO.findAll());
-        } else {
-            for (int page = 1; page <= (count() / PAGE_SIZE) + 1; page++) {
-                List<User> users = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
+        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            List<User> users;
+            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
+                users = userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
+            } else {
+                users = searchDAO.search(
+                        SyncopeConstants.FULL_ADMIN_REALMS,
                         SearchCondConverter.convert(this.conf.getMatchingCond()),
-                        page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), AnyTypeKind.USER);
-                doExtract(handler, users);
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        AnyTypeKind.USER);
             }
+
+            doExtract(handler, users);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
index 6b2fd4e..44c0c8c 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
@@ -28,6 +28,8 @@ import org.apache.syncope.core.persistence.api.entity.Schema;
 
 public interface AnyDAO<A extends Any<?>> extends DAO<A> {
 
+    int DEFAULT_PAGE_SIZE = 10;
+
     A authFind(String key);
 
     A find(String key);
@@ -53,11 +55,18 @@ public interface AnyDAO<A extends Any<?>> extends DAO<A> {
     List<A> findByResource(ExternalResource resource);
 
     /**
-     * Find any objects without any limitation.
+     * @return the total number of any objects of type {@link A}
+     */
+    int count();
+
+    /**
+     * Find any objects without any limitation, according to given page and items per page.
      *
-     * @return all any objects of type {@link A} available.
+     * @param page search result page
+     * @param itemsPerPage items per search result page
+     * @return any objects of type {@link A} matching the provided conditions
      */
-    List<A> findAll();
+    List<A> findAll(int page, int itemsPerPage);
 
     /**
      * Find any objects visible from the given admin realms, according to given page and items per page, sorted as

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
index d399ac8..c3a4cd7 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
@@ -26,7 +26,6 @@ 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.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.user.URelationship;
 
 public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
@@ -46,9 +45,7 @@ public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
     List<Group> findDynGroupMemberships(AnyObject anyObject);
 
-    List<ARelationship> findARelationships(AnyObject anyObject);
-
-    List<URelationship> findURelationships(AnyObject anyObject);
+    List<ARelationship> findAllARelationships(AnyObject anyObject);
 
     Collection<Group> findAllGroups(AnyObject anyObject);
 
@@ -56,5 +53,5 @@ public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
     Collection<ExternalResource> findAllResources(AnyObject anyObject);
 
-    Collection<String> findAllResourceNames(String key);
+    Collection<String> findAllResourceKeys(String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java
index aa30108..c74e430 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java
@@ -30,8 +30,6 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 
 public interface GroupDAO extends AnyDAO<Group> {
 
-    int count();
-
     Map<String, Integer> countByRealm();
 
     Group findByName(String name);

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
index d577327..55efa93 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
@@ -30,8 +30,6 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 
 public interface UserDAO extends AnyDAO<User> {
 
-    int count();
-
     Map<String, Integer> countByRealm();
 
     Map<String, Integer> countByStatus();
@@ -58,7 +56,7 @@ public interface UserDAO extends AnyDAO<User> {
 
     Collection<ExternalResource> findAllResources(User user);
 
-    Collection<String> findAllResourceNames(String key);
+    Collection<String> findAllResourceKeys(String key);
 
     Pair<Boolean, Boolean> enforcePolicies(User user);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/pom.xml b/core/persistence-jpa/pom.xml
index 68a13fd..c653f23 100644
--- a/core/persistence-jpa/pom.xml
+++ b/core/persistence-jpa/pom.xml
@@ -98,11 +98,6 @@ under the License.
     </dependency>
       
     <dependency>
-      <groupId>org.quartz-scheduler</groupId>
-      <artifactId>quartz</artifactId>
-    </dependency>
-      
-    <dependency>
       <groupId>org.apache.syncope.core</groupId>
       <artifactId>syncope-core-workflow-api</artifactId>
       <version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
index 25bfe50..f348a06 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
@@ -36,7 +36,6 @@ import org.apache.commons.jexl3.parser.Parser;
 import org.apache.commons.jexl3.parser.ParserConstants;
 import org.apache.commons.jexl3.parser.Token;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.persistence.api.dao.AllowedSchemas;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
@@ -64,11 +63,16 @@ import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractPlainAttrValue;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> implements AnyDAO<A> {
 
+    @Autowired
+    protected ApplicationEventPublisher publisher;
+
     private PlainSchemaDAO plainSchemaDAO;
 
     private DerSchemaDAO derSchemaDAO;
@@ -425,11 +429,6 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
         return query.getResultList();
     }
 
-    @Override
-    public final List<A> findAll() {
-        return findAll(SyncopeConstants.FULL_ADMIN_REALMS, -1, -1, Collections.<OrderByClause>emptyList());
-    }
-
     private SearchCond getAllMatchingCond() {
         AnyCond idCond = new AnyCond(AttributeCond.Type.ISNOTNULL);
         idCond.setSchema("id");

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
new file mode 100644
index 0000000..d9b4e95
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
@@ -0,0 +1,343 @@
+/*
+ * 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.persistence.jpa.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.persistence.Entity;
+import javax.validation.ValidationException;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+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.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
+import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+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.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+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.persistence.jpa.entity.JPAPlainSchema;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ReflectionUtils;
+
+public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO {
+
+    @Autowired
+    protected RealmDAO realmDAO;
+
+    @Autowired
+    protected AnyObjectDAO anyObjectDAO;
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected GroupDAO groupDAO;
+
+    @Autowired
+    protected PlainSchemaDAO schemaDAO;
+
+    @Autowired
+    protected AnyUtilsFactory anyUtilsFactory;
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public <T extends Any<?>> List<T> searchAssignable(final String realmFullPath, final AnyTypeKind kind) {
+        AssignableCond assignableCond = new AssignableCond();
+        assignableCond.setRealmFullPath(realmFullPath);
+        return search(SearchCond.getLeafCond(assignableCond), kind);
+    }
+
+    protected abstract int doCount(Set<String> adminRealms, SearchCond cond, AnyTypeKind kind);
+
+    @Override
+    public int count(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
+        if (adminRealms == null || adminRealms.isEmpty()) {
+            LOG.error("No realms provided");
+            return 0;
+        }
+
+        LOG.debug("Search condition:\n{}", cond);
+        if (cond == null || !cond.isValid()) {
+            LOG.error("Invalid search condition:\n{}", cond);
+            return 0;
+        }
+
+        return doCount(adminRealms, cond, kind);
+    }
+
+    @Override
+    public <T extends Any<?>> List<T> search(final SearchCond cond, final AnyTypeKind kind) {
+        return search(cond, Collections.<OrderByClause>emptyList(), kind);
+    }
+
+    @Override
+    public <T extends Any<?>> List<T> search(
+            final SearchCond cond, final List<OrderByClause> orderBy, final AnyTypeKind kind) {
+
+        return search(SyncopeConstants.FULL_ADMIN_REALMS, cond, -1, -1, orderBy, kind);
+    }
+
+    protected abstract <T extends Any<?>> List<T> doSearch(
+            Set<String> adminRealms,
+            SearchCond searchCondition,
+            int page,
+            int itemsPerPage,
+            List<OrderByClause> orderBy,
+            AnyTypeKind kind);
+
+    protected Pair<PlainSchema, PlainAttrValue> check(final AttributeCond cond, final AnyTypeKind kind) {
+        AnyUtils attrUtils = anyUtilsFactory.getInstance(kind);
+
+        PlainSchema schema = schemaDAO.find(cond.getSchema());
+        if (schema == null) {
+            LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
+            throw new IllegalArgumentException();
+        }
+
+        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        try {
+            if (cond.getType() != AttributeCond.Type.LIKE
+                    && cond.getType() != AttributeCond.Type.ILIKE
+                    && cond.getType() != AttributeCond.Type.ISNULL
+                    && cond.getType() != AttributeCond.Type.ISNOTNULL) {
+
+                schema.getValidator().validate(cond.getExpression(), attrValue);
+            }
+        } catch (ValidationException e) {
+            LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
+            throw new IllegalArgumentException();
+        }
+
+        return Pair.of(schema, attrValue);
+    }
+
+    protected Triple<PlainSchema, PlainAttrValue, AnyCond> check(final AnyCond cond, final AnyTypeKind kind) {
+        AnyCond condClone = SerializationUtils.clone(cond);
+
+        AnyUtils attrUtils = anyUtilsFactory.getInstance(kind);
+
+        // Keeps track of difference between entity's getKey() and JPA @Id fields
+        if ("key".equals(condClone.getSchema())) {
+            condClone.setSchema("id");
+        }
+
+        Field anyField = ReflectionUtils.findField(attrUtils.anyClass(), condClone.getSchema());
+        if (anyField == null) {
+            LOG.warn("Ignoring invalid schema '{}'", condClone.getSchema());
+            throw new IllegalArgumentException();
+        }
+
+        PlainSchema schema = new JPAPlainSchema();
+        schema.setKey(anyField.getName());
+        for (AttrSchemaType attrSchemaType : AttrSchemaType.values()) {
+            if (anyField.getType().isAssignableFrom(attrSchemaType.getType())) {
+                schema.setType(attrSchemaType);
+            }
+        }
+
+        // Deal with any Integer fields logically mapping to boolean values
+        boolean foundBooleanMin = false;
+        boolean foundBooleanMax = false;
+        if (Integer.class.equals(anyField.getType())) {
+            for (Annotation annotation : anyField.getAnnotations()) {
+                if (Min.class.equals(annotation.annotationType())) {
+                    foundBooleanMin = ((Min) annotation).value() == 0;
+                } else if (Max.class.equals(annotation.annotationType())) {
+                    foundBooleanMax = ((Max) annotation).value() == 1;
+                }
+            }
+        }
+        if (foundBooleanMin && foundBooleanMax) {
+            schema.setType(AttrSchemaType.Boolean);
+        }
+
+        // Deal with any fields representing relationships to other entities
+        if (anyField.getType().getAnnotation(Entity.class) != null) {
+            Method relMethod = null;
+            try {
+                relMethod = ClassUtils.getPublicMethod(anyField.getType(), "getKey", new Class<?>[0]);
+            } catch (Exception e) {
+                LOG.error("Could not find {}#getKey", anyField.getType(), e);
+            }
+
+            if (relMethod != null && String.class.isAssignableFrom(relMethod.getReturnType())) {
+                condClone.setSchema(condClone.getSchema() + "_id");
+                schema.setType(AttrSchemaType.String);
+            }
+        }
+
+        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        if (condClone.getType() != AttributeCond.Type.LIKE
+                && condClone.getType() != AttributeCond.Type.ILIKE
+                && condClone.getType() != AttributeCond.Type.ISNULL
+                && condClone.getType() != AttributeCond.Type.ISNOTNULL) {
+
+            try {
+                schema.getValidator().validate(condClone.getExpression(), attrValue);
+            } catch (ValidationException e) {
+                LOG.error("Could not validate expression '" + condClone.getExpression() + "'", e);
+                throw new IllegalArgumentException();
+            }
+        }
+
+        return Triple.of(schema, attrValue, condClone);
+    }
+
+    protected String check(final MembershipCond cond) {
+        String groupKey;
+        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getGroup()).matches()) {
+            groupKey = cond.getGroup();
+        } else {
+            Group group = groupDAO.findByName(cond.getGroup());
+            groupKey = group == null ? null : group.getKey();
+        }
+        if (groupKey == null) {
+            LOG.error("Could not find group for '" + cond.getGroup() + "'");
+            throw new IllegalArgumentException();
+        }
+
+        return groupKey;
+    }
+
+    protected String check(final RelationshipCond cond) {
+        String rightAnyObjectKey;
+        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getAnyObject()).matches()) {
+            rightAnyObjectKey = cond.getAnyObject();
+        } else {
+            AnyObject anyObject = anyObjectDAO.findByName(cond.getAnyObject());
+            rightAnyObjectKey = anyObject == null ? null : anyObject.getKey();
+        }
+        if (rightAnyObjectKey == null) {
+            LOG.error("Could not find any object for '" + cond.getAnyObject() + "'");
+            throw new IllegalArgumentException();
+        }
+
+        return rightAnyObjectKey;
+    }
+
+    protected Realm check(final AssignableCond cond) {
+        Realm realm = realmDAO.findByFullPath(cond.getRealmFullPath());
+        if (realm == null) {
+            LOG.error("Could not find realm for '" + cond.getRealmFullPath() + "'");
+            throw new IllegalArgumentException();
+        }
+
+        return realm;
+    }
+
+    protected String check(final MemberCond cond) {
+        String memberKey;
+        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getMember()).matches()) {
+            memberKey = cond.getMember();
+        } else {
+            Any<?> member = userDAO.findByUsername(cond.getMember());
+            if (member == null) {
+                member = anyObjectDAO.findByName(cond.getMember());
+            }
+            memberKey = member == null ? null : member.getKey();
+        }
+        if (memberKey == null) {
+            LOG.error("Could not find user or any object for '" + cond.getMember() + "'");
+            throw new IllegalArgumentException();
+        }
+
+        return memberKey;
+    }
+
+    protected <T extends Any<?>> List<T> buildResult(final List<Object> raw, final AnyTypeKind kind) {
+        List<T> result = new ArrayList<>();
+
+        for (Object anyKey : raw) {
+            String actualKey = anyKey instanceof Object[]
+                    ? (String) ((Object[]) anyKey)[0]
+                    : ((String) anyKey);
+
+            @SuppressWarnings("unchecked")
+            T any = kind == AnyTypeKind.USER
+                    ? (T) userDAO.find(actualKey)
+                    : kind == AnyTypeKind.GROUP
+                            ? (T) groupDAO.find(actualKey)
+                            : (T) anyObjectDAO.find(actualKey);
+            if (any == null) {
+                LOG.error("Could not find {} with id {}, even if returned by native query", kind, actualKey);
+            } else if (!result.contains(any)) {
+                result.add(any);
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public <T extends Any<?>> List<T> search(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final int page,
+            final int itemsPerPage,
+            final List<OrderByClause> orderBy,
+            final AnyTypeKind kind) {
+
+        if (adminRealms == null || adminRealms.isEmpty()) {
+            LOG.error("No realms provided");
+            return Collections.<T>emptyList();
+        }
+
+        LOG.debug("Search condition:\n{}", cond);
+        if (cond == null || !cond.isValid()) {
+            LOG.error("Invalid search condition:\n{}", cond);
+            return Collections.<T>emptyList();
+        }
+
+        return doSearch(adminRealms, cond, page, itemsPerPage, orderBy, kind);
+    }
+
+    @Override
+    public <T extends Any<?>> boolean matches(final T any, final SearchCond cond) {
+        return search(cond, any.getType().getKind()).contains(any);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
index 164f9a3..a64afe3 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
@@ -59,6 +59,8 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.event.AnyDeletedEvent;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -175,21 +177,28 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     }
 
     @Override
-    public List<ARelationship> findARelationships(final AnyObject anyObject) {
+    public List<ARelationship> findAllARelationships(final AnyObject anyObject) {
         TypedQuery<ARelationship> query = entityManager().createQuery(
                 "SELECT e FROM " + JPAARelationship.class.getSimpleName()
-                + " e WHERE e.rightEnd=:anyObject", ARelationship.class);
+                + " e WHERE e.rightEnd=:anyObject OR e.leftEnd=:anyObject", ARelationship.class);
         query.setParameter("anyObject", anyObject);
 
         return query.getResultList();
     }
 
     @Override
-    public List<URelationship> findURelationships(final AnyObject anyObject) {
-        TypedQuery<URelationship> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAURelationship.class.getSimpleName()
-                + " e WHERE e.rightEnd=:anyObject", URelationship.class);
-        query.setParameter("anyObject", anyObject);
+    public int count() {
+        Query query = entityManager().createQuery(
+                "SELECT COUNT(e) FROM  " + JPAAnyObject.class.getSimpleName() + " e");
+        return ((Number) query.getSingleResult()).intValue();
+    }
+
+    @Override
+    public List<AnyObject> findAll(final int page, final int itemsPerPage) {
+        TypedQuery<AnyObject> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAAnyObject.class.getSimpleName() + " e", AnyObject.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
 
         return query.getResultList();
     }
@@ -197,12 +206,31 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public AnyObject save(final AnyObject anyObject) {
         AnyObject merged = super.save(anyObject);
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
 
         groupDAO().refreshDynMemberships(merged);
 
         return merged;
     }
 
+    private List<ARelationship> findARelationships(final AnyObject anyObject) {
+        TypedQuery<ARelationship> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAARelationship.class.getSimpleName()
+                + " e WHERE e.rightEnd=:anyObject", ARelationship.class);
+        query.setParameter("anyObject", anyObject);
+
+        return query.getResultList();
+    }
+
+    private List<URelationship> findURelationships(final AnyObject anyObject) {
+        TypedQuery<URelationship> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAURelationship.class.getSimpleName()
+                + " e WHERE e.rightEnd=:anyObject", URelationship.class);
+        query.setParameter("anyObject", anyObject);
+
+        return query.getResultList();
+    }
+
     @Override
     public void delete(final AnyObject any) {
         for (Group group : findDynGroupMemberships(any)) {
@@ -223,6 +251,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
         }
 
         entityManager().remove(any);
+        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.ANY_OBJECT, any.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
@@ -287,7 +316,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public Collection<String> findAllResourceNames(final String key) {
+    public Collection<String> findAllResourceKeys(final String key) {
         return CollectionUtils.collect(findAllResources(authFind(key)), EntityUtils.<ExternalResource>keyTransformer());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 49bf4ff..276b570 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -18,36 +18,23 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import javax.persistence.Entity;
 import javax.persistence.Query;
 import javax.persistence.TemporalType;
-import javax.validation.ValidationException;
-import javax.validation.constraints.Max;
-import javax.validation.constraints.Min;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.ClassUtils;
-import org.apache.commons.lang3.SerializationUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
-import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -62,42 +49,18 @@ import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 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.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-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.persistence.jpa.entity.JPAPlainSchema;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ReflectionUtils;
 
-@Repository
-public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO {
+/**
+ * Search engine implementation for users, groups and any objects, based on self-updating SQL views.
+ */
+public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
     private static final String EMPTY_QUERY = "SELECT any_id FROM user_search_attr WHERE 1=2";
 
-    @Autowired
-    private RealmDAO realmDAO;
-
-    @Autowired
-    private AnyObjectDAO anyObjectDAO;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private GroupDAO groupDAO;
-
-    @Autowired
-    private PlainSchemaDAO schemaDAO;
-
-    @Autowired
-    private AnyUtilsFactory anyUtilsFactory;
-
     private String getAdminRealmsFilter(
             final Set<String> adminRealms,
             final SearchSupport svs,
@@ -135,11 +98,11 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
     }
 
     @Override
-    public int count(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind typeKind) {
+    protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
         List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
 
         // 1. get the query string from the search condition
-        SearchSupport svs = new SearchSupport(typeKind);
+        SearchSupport svs = new SearchSupport(kind);
         StringBuilder queryString = getQuery(cond, parameters, svs);
 
         // 2. take into account administrative realms
@@ -156,78 +119,56 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
         return ((Number) countQuery.getSingleResult()).intValue();
     }
 
-    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
-    @Override
-    public <T extends Any<?>> List<T> searchAssignable(final String realmFullPath, final AnyTypeKind kind) {
-        AssignableCond assignableCond = new AssignableCond();
-        assignableCond.setRealmFullPath(realmFullPath);
-        return search(SearchCond.getLeafCond(assignableCond), kind);
-    }
-
-    @Override
-    public <T extends Any<?>> List<T> search(final SearchCond cond, final AnyTypeKind typeKind) {
-        return search(cond, Collections.<OrderByClause>emptyList(), typeKind);
-    }
-
     @Override
-    public <T extends Any<?>> List<T> search(
-            final SearchCond cond, final List<OrderByClause> orderBy, final AnyTypeKind typeKind) {
-
-        return search(SyncopeConstants.FULL_ADMIN_REALMS, cond, -1, -1, orderBy, typeKind);
-    }
-
-    @Override
-    public <T extends Any<?>> List<T> search(
-            final Set<String> adminRealms, final SearchCond cond, final int page, final int itemsPerPage,
-            final List<OrderByClause> orderBy, final AnyTypeKind typeKind) {
+    @SuppressWarnings("unchecked")
+    protected <T extends Any<?>> List<T> doSearch(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final int page,
+            final int itemsPerPage,
+            final List<OrderByClause> orderBy,
+            final AnyTypeKind kind) {
 
-        List<T> result = Collections.<T>emptyList();
+        try {
+            List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
 
-        if (adminRealms != null && !adminRealms.isEmpty()) {
-            LOG.debug("Search condition:\n{}", cond);
+            // 1. get the query string from the search condition
+            SearchSupport svs = new SearchSupport(kind);
+            StringBuilder queryString = getQuery(cond, parameters, svs);
 
-            if (cond != null && cond.isValid()) {
-                try {
-                    result = doSearch(adminRealms, cond, page, itemsPerPage, orderBy, typeKind);
-                } catch (Exception e) {
-                    LOG.error("While searching for {}", typeKind, e);
-                }
+            // 2. take into account realms and ordering
+            OrderBySupport obs = parseOrderBy(kind, svs, orderBy);
+            if (queryString.charAt(0) == '(') {
+                queryString.insert(0, buildSelect(obs));
+                queryString.append(buildWhere(svs, obs));
             } else {
-                LOG.error("Invalid search condition:\n{}", cond);
+                queryString.insert(0, buildSelect(obs).append('('));
+                queryString.append(')').append(buildWhere(svs, obs));
             }
-        }
-
-        return result;
-    }
-
-    @Override
-    public <T extends Any<?>> boolean matches(final T any, final SearchCond cond) {
-        List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
-
-        // 1. get the query string from the search condition
-        SearchSupport svs = new SearchSupport(any.getType().getKind());
-        StringBuilder queryString = getQuery(cond, parameters, svs);
-
-        boolean matches;
-        if (queryString.length() == 0) {
-            // Could be empty: got into a group search with a single membership condition ...
-            matches = false;
-        } else {
-            // 2. take into account the passed user
-            queryString.insert(0, "SELECT u.any_id FROM (");
-            queryString.append(") u WHERE any_id=?").append(setParameter(parameters, any.getKey()));
+            queryString.
+                    append(getAdminRealmsFilter(adminRealms, svs, parameters)).
+                    append(buildOrderBy(obs));
 
             // 3. prepare the search query
             Query query = entityManager().createNativeQuery(queryString.toString());
 
-            // 4. populate the search query with parameter values
+            // 4. page starts from 1, while setFirtResult() starts from 0
+            query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+
+            if (itemsPerPage >= 0) {
+                query.setMaxResults(itemsPerPage);
+            }
+
+            // 5. populate the search query with parameter values
             fillWithParameters(query, parameters);
 
-            // 5. executes query
-            matches = !query.getResultList().isEmpty();
+            // 6. Prepare the result (avoiding duplicates)
+            return buildResult(query.getResultList(), kind);
+        } catch (Exception e) {
+            LOG.error("While searching for {}", kind, e);
         }
 
-        return matches;
+        return Collections.emptyList();
     }
 
     private int setParameter(final List<Object> parameters, final Object parameter) {
@@ -370,67 +311,6 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
         return obs;
     }
 
-    @SuppressWarnings("unchecked")
-    private <T extends Any<?>> List<T> doSearch(final Set<String> adminRealms,
-            final SearchCond cond, final int page, final int itemsPerPage, final List<OrderByClause> orderBy,
-            final AnyTypeKind typeKind) {
-
-        List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
-
-        // 1. get the query string from the search condition
-        SearchSupport svs = new SearchSupport(typeKind);
-        StringBuilder queryString = getQuery(cond, parameters, svs);
-
-        // 2. take into account administrative groups and ordering
-        OrderBySupport obs = parseOrderBy(typeKind, svs, orderBy);
-        if (queryString.charAt(0) == '(') {
-            queryString.insert(0, buildSelect(obs));
-            queryString.append(buildWhere(svs, obs));
-        } else {
-            queryString.insert(0, buildSelect(obs).append('('));
-            queryString.append(')').append(buildWhere(svs, obs));
-        }
-        queryString.
-                append(getAdminRealmsFilter(adminRealms, svs, parameters)).
-                append(buildOrderBy(obs));
-
-        // 3. prepare the search query
-        Query query = entityManager().createNativeQuery(queryString.toString());
-
-        // 4. page starts from 1, while setFirtResult() starts from 0
-        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
-
-        if (itemsPerPage >= 0) {
-            query.setMaxResults(itemsPerPage);
-        }
-
-        // 5. populate the search query with parameter values
-        fillWithParameters(query, parameters);
-
-        // 6. Prepare the result (avoiding duplicates)
-        List<T> result = new ArrayList<>();
-
-        for (Object anyKey : query.getResultList()) {
-            String actualKey = anyKey instanceof Object[]
-                    ? (String) ((Object[]) anyKey)[0]
-                    : ((String) anyKey);
-
-            T any = typeKind == AnyTypeKind.USER
-                    ? (T) userDAO.find(actualKey)
-                    : typeKind == AnyTypeKind.GROUP
-                            ? (T) groupDAO.find(actualKey)
-                            : (T) anyObjectDAO.find(actualKey);
-            if (any == null) {
-                LOG.error("Could not find {} with id {}, even though returned by the native query",
-                        typeKind, actualKey);
-            } else if (!result.contains(any)) {
-                result.add(any);
-            }
-        }
-
-        return result;
-    }
-
     private StringBuilder getQuery(final SearchCond cond, final List<Object> parameters, final SearchSupport svs) {
         StringBuilder query = new StringBuilder();
 
@@ -548,13 +428,9 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
             final RelationshipCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
         String rightAnyObjectKey;
-        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getAnyObject()).matches()) {
-            rightAnyObjectKey = cond.getAnyObject();
-        } else {
-            AnyObject anyObject = anyObjectDAO.findByName(cond.getAnyObject());
-            rightAnyObjectKey = anyObject == null ? null : anyObject.getKey();
-        }
-        if (rightAnyObjectKey == null) {
+        try {
+            rightAnyObjectKey = check(cond);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -579,13 +455,9 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
             final MembershipCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
         String groupKey;
-        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getGroup()).matches()) {
-            groupKey = cond.getGroup();
-        } else {
-            Group group = groupDAO.findByName(cond.getGroup());
-            groupKey = group == null ? null : group.getKey();
-        }
-        if (groupKey == null) {
+        try {
+            groupKey = check(cond);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -678,8 +550,10 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
     }
 
     private String getQuery(final AssignableCond cond, final List<Object> parameters, final SearchSupport svs) {
-        Realm realm = realmDAO.findByFullPath(cond.getRealmFullPath());
-        if (realm == null) {
+        Realm realm;
+        try {
+            realm = check(cond);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -705,16 +579,9 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
             final MemberCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
         String memberKey;
-        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getMember()).matches()) {
-            memberKey = cond.getMember();
-        } else {
-            Any<?> member = userDAO.findByUsername(cond.getMember());
-            if (member == null) {
-                member = anyObjectDAO.findByName(cond.getMember());
-            }
-            memberKey = member == null ? null : member.getKey();
-        }
-        if (memberKey == null) {
+        try {
+            memberKey = check(cond);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -746,7 +613,7 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
         return query.toString();
     }
 
-    private void fillAttributeQuery(
+    private void fillAttrQuery(
             final StringBuilder query,
             final PlainAttrValue attrValue,
             final PlainSchema schema,
@@ -864,28 +731,10 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
     private String getQuery(
             final AttributeCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
-        AnyUtils attrUtils = anyUtilsFactory.getInstance(svs.anyTypeKind);
-
-        PlainSchema schema = schemaDAO.find(cond.getSchema());
-        if (schema == null) {
-            LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
-            return EMPTY_QUERY;
-        }
-
-        // keep track of involvement of non-mandatory schemas in the search condition
-        svs.nonMandatorySchemas = !"true".equals(schema.getMandatoryCondition());
-
-        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        Pair<PlainSchema, PlainAttrValue> checked;
         try {
-            if (cond.getType() != AttributeCond.Type.LIKE
-                    && cond.getType() != AttributeCond.Type.ILIKE
-                    && cond.getType() != AttributeCond.Type.ISNULL
-                    && cond.getType() != AttributeCond.Type.ISNOTNULL) {
-
-                schema.getValidator().validate(cond.getExpression(), attrValue);
-            }
-        } catch (ValidationException e) {
-            LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
+            checked = check(cond, svs.anyTypeKind);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -895,22 +744,22 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
                 query.append(svs.field().name).
                         append(" WHERE any_id NOT IN (SELECT any_id FROM ").
                         append(svs.nullAttr().name).
-                        append(" WHERE schema_id='").append(schema.getKey()).append("')");
+                        append(" WHERE schema_id='").append(checked.getLeft().getKey()).append("')");
                 break;
 
             case ISNULL:
                 query.append(svs.nullAttr().name).
-                        append(" WHERE schema_id='").append(schema.getKey()).append("'");
+                        append(" WHERE schema_id='").append(checked.getLeft().getKey()).append("'");
                 break;
 
             default:
-                if (schema.isUniqueConstraint()) {
+                if (checked.getLeft().isUniqueConstraint()) {
                     query.append(svs.uniqueAttr().name);
                 } else {
                     query.append(svs.attr().name);
                 }
-                query.append(" WHERE schema_id='").append(schema.getKey());
-                fillAttributeQuery(query, attrValue, schema, cond, not, parameters, svs);
+                query.append(" WHERE schema_id='").append(checked.getLeft().getKey());
+                fillAttrQuery(query, checked.getRight(), checked.getLeft(), cond, not, parameters, svs);
         }
 
         return query.toString();
@@ -919,78 +768,17 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
     private String getQuery(
             final AnyCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
-        AnyCond condClone = SerializationUtils.clone(cond);
-
-        AnyUtils attrUtils = anyUtilsFactory.getInstance(svs.anyTypeKind);
-
-        // Keeps track of difference between entity's getKey() and JPA @Id fields
-        if ("key".equals(condClone.getSchema())) {
-            condClone.setSchema("id");
-        }
-
-        Field anyField = ReflectionUtils.findField(attrUtils.anyClass(), condClone.getSchema());
-        if (anyField == null) {
-            LOG.warn("Ignoring invalid schema '{}'", condClone.getSchema());
+        Triple<PlainSchema, PlainAttrValue, AnyCond> checked;
+        try {
+            checked = check(cond, svs.anyTypeKind);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
-        PlainSchema schema = new JPAPlainSchema();
-        schema.setKey(anyField.getName());
-        for (AttrSchemaType attrSchemaType : AttrSchemaType.values()) {
-            if (anyField.getType().isAssignableFrom(attrSchemaType.getType())) {
-                schema.setType(attrSchemaType);
-            }
-        }
-
-        // Deal with any Integer fields logically mapping to boolean values
-        boolean foundBooleanMin = false;
-        boolean foundBooleanMax = false;
-        if (Integer.class.equals(anyField.getType())) {
-            for (Annotation annotation : anyField.getAnnotations()) {
-                if (Min.class.equals(annotation.annotationType())) {
-                    foundBooleanMin = ((Min) annotation).value() == 0;
-                } else if (Max.class.equals(annotation.annotationType())) {
-                    foundBooleanMax = ((Max) annotation).value() == 1;
-                }
-            }
-        }
-        if (foundBooleanMin && foundBooleanMax) {
-            schema.setType(AttrSchemaType.Boolean);
-        }
-
-        // Deal with any fields representing relationships to other entities
-        if (anyField.getType().getAnnotation(Entity.class) != null) {
-            Method relMethod = null;
-            try {
-                relMethod = ClassUtils.getPublicMethod(anyField.getType(), "getKey", new Class<?>[0]);
-            } catch (Exception e) {
-                LOG.error("Could not find {}#getKey", anyField.getType(), e);
-            }
-
-            if (relMethod != null && String.class.isAssignableFrom(relMethod.getReturnType())) {
-                condClone.setSchema(condClone.getSchema() + "_id");
-                schema.setType(AttrSchemaType.String);
-            }
-        }
-
-        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
-        if (condClone.getType() != AttributeCond.Type.LIKE
-                && condClone.getType() != AttributeCond.Type.ILIKE
-                && condClone.getType() != AttributeCond.Type.ISNULL
-                && condClone.getType() != AttributeCond.Type.ISNOTNULL) {
-
-            try {
-                schema.getValidator().validate(condClone.getExpression(), attrValue);
-            } catch (ValidationException e) {
-                LOG.error("Could not validate expression '" + condClone.getExpression() + "'", e);
-                return EMPTY_QUERY;
-            }
-        }
-
         StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
                 append(svs.field().name).append(" WHERE ");
 
-        fillAttributeQuery(query, attrValue, schema, condClone, not, parameters, svs);
+        fillAttrQuery(query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, parameters, svs);
 
         return query.toString();
     }


[6/8] syncope git commit: [SYNCOPE-1077] Extension provided, not enabled by default in fit/core-reference (profile available)

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml b/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
new file mode 100644
index 0000000..f870317
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchClientFactoryBean">
+    <property name="settings">
+      <map>
+        <entry key="cluster.name">
+          <value>elasticsearch</value>
+        </entry>
+        <entry key="client.transport.sniff">
+          <value>true</value>
+        </entry>
+      </map>
+    </property>  
+    <property name="addresses">
+      <map>
+        <entry key="localhost">
+          <value>9300</value>
+        </entry>
+      </map>
+    </property>
+  </bean>
+  
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils">
+    <property name="indexMaxResultWindow" value="10000"/>
+  </bean>
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager"/>
+  
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/pom.xml b/ext/elasticsearch/persistence-jpa/pom.xml
new file mode 100644
index 0000000..129b36b
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-elasticsearch</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch Persistence JPA</name>
+  <description>Apache Syncope Ext: Elasticsearch Persistence JPA</description>
+  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+  <artifactId>syncope-ext-elasticsearch-persistence-jpa</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>    
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-persistence-jpa</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+      <artifactId>syncope-ext-elasticsearch-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
new file mode 100644
index 0000000..34651e1
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
@@ -0,0 +1,355 @@
+/*
+ * 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.persistence.jpa.dao;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
+import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.ResourceCond;
+import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchType;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.index.query.DisMaxQueryBuilder;
+import org.elasticsearch.index.query.MatchNoneQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Search engine implementation for users, groups and any objects, based on Elasticsearch.
+ */
+public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
+
+    private static final QueryBuilder EMPTY_QUERY_BUILDER = new MatchNoneQueryBuilder();
+
+    @Autowired
+    private Client client;
+
+    @Autowired
+    private ElasticsearchUtils elasticsearchUtils;
+
+    private DisMaxQueryBuilder adminRealmsFilter(final Set<String> adminRealms) {
+        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
+
+        for (String realmPath : RealmUtils.normalize(adminRealms)) {
+            Realm realm = realmDAO.findByFullPath(realmPath);
+            if (realm == null) {
+                LOG.warn("Ignoring invalid realm {}", realmPath);
+            } else {
+                for (Realm descendant : realmDAO.findDescendants(realm)) {
+                    builder.add(QueryBuilders.termQuery("realm.keyword", descendant.getFullPath()));
+                }
+            }
+        }
+
+        return builder;
+    }
+
+    private SearchRequestBuilder searchRequestBuilder(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final AnyTypeKind kind) {
+
+        return client.prepareSearch(AuthContextUtils.getDomain().toLowerCase()).
+                setTypes(kind.name()).
+                setSearchType(SearchType.QUERY_THEN_FETCH).
+                setQuery(SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms)
+                        ? getQueryBuilder(cond, kind)
+                        : QueryBuilders.boolQuery().
+                                must(adminRealmsFilter(adminRealms)).
+                                must(getQueryBuilder(cond, kind)));
+    }
+
+    @Override
+    protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
+        SearchRequestBuilder builder = searchRequestBuilder(adminRealms, cond, kind).
+                setFrom(0).setSize(0);
+
+        return (int) builder.get().getHits().getTotalHits();
+    }
+
+    @Override
+    protected <T extends Any<?>> List<T> doSearch(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final int page,
+            final int itemsPerPage,
+            final List<OrderByClause> orderBy,
+            final AnyTypeKind kind) {
+
+        SearchRequestBuilder builder = searchRequestBuilder(adminRealms, cond, kind).
+                setFrom(page <= 0 ? 0 : page - 1).
+                setSize(itemsPerPage < 0 ? elasticsearchUtils.getIndexMaxResultWindow() : itemsPerPage);
+
+        return buildResult(
+                CollectionUtils.collect(Arrays.asList(builder.get().getHits().getHits()),
+                        new Transformer<SearchHit, Object>() {
+
+                    @Override
+                    public Object transform(final SearchHit input) {
+                        return input.getId();
+                    }
+                }, new ArrayList<>()),
+                kind);
+    }
+
+    private QueryBuilder getQueryBuilder(final SearchCond cond, final AnyTypeKind kind) {
+        QueryBuilder builder = EMPTY_QUERY_BUILDER;
+
+        switch (cond.getType()) {
+            case LEAF:
+            case NOT_LEAF:
+                if (cond.getAnyTypeCond() != null && AnyTypeKind.ANY_OBJECT == kind) {
+                    builder = getQueryBuilder(cond.getAnyTypeCond());
+                } else if (cond.getRelationshipTypeCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getRelationshipTypeCond());
+                } else if (cond.getRelationshipCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getRelationshipCond());
+                } else if (cond.getMembershipCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getMembershipCond());
+                } else if (cond.getAssignableCond() != null) {
+                    builder = getQueryBuilder(cond.getAssignableCond());
+                } else if (cond.getRoleCond() != null && AnyTypeKind.USER == kind) {
+                    builder = getQueryBuilder(cond.getRoleCond());
+                } else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == kind) {
+                    builder = getQueryBuilder(cond.getMemberCond());
+                } else if (cond.getResourceCond() != null) {
+                    builder = getQueryBuilder(cond.getResourceCond());
+                } else if (cond.getAttributeCond() != null) {
+                    builder = getQueryBuilder(cond.getAttributeCond(), kind);
+                } else if (cond.getAnyCond() != null) {
+                    builder = getQueryBuilder(cond.getAnyCond(), kind);
+                }
+                builder = checkNot(builder, cond.getType() == SearchCond.Type.NOT_LEAF);
+                break;
+
+            case AND:
+                builder = QueryBuilders.boolQuery().
+                        must(getQueryBuilder(cond.getLeftSearchCond(), kind)).
+                        must(getQueryBuilder(cond.getRightSearchCond(), kind));
+                break;
+
+            case OR:
+                builder = QueryBuilders.disMaxQuery().
+                        add(getQueryBuilder(cond.getLeftSearchCond(), kind)).
+                        add(getQueryBuilder(cond.getRightSearchCond(), kind));
+                break;
+
+            default:
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder checkNot(final QueryBuilder builder, final boolean not) {
+        return not
+                ? QueryBuilders.boolQuery().mustNot(builder)
+                : builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final AnyTypeCond cond) {
+        return QueryBuilders.termQuery("anyType.keyword", cond.getAnyTypeKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final RelationshipTypeCond cond) {
+        return QueryBuilders.termQuery("relationshipTypes.keyword", cond.getRelationshipTypeKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final RelationshipCond cond) {
+        String rightAnyObjectKey;
+        try {
+            rightAnyObjectKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("relationships.keyword", rightAnyObjectKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final MembershipCond cond) {
+        String groupKey;
+        try {
+            groupKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("memberships.keyword", groupKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final AssignableCond cond) {
+        Realm realm;
+        try {
+            realm = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
+        if (cond.isFromGroup()) {
+            for (Realm current = realm; current.getParent() != null; current = current.getParent()) {
+                builder.add(QueryBuilders.termQuery("realm.keyword", current.getFullPath()));
+            }
+            builder.add(QueryBuilders.termQuery("realm.keyword", realmDAO.getRoot().getFullPath()));
+        } else {
+            for (Realm current : realmDAO.findDescendants(realm)) {
+                builder.add(QueryBuilders.termQuery("realm.keyword", current.getFullPath()));
+            }
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final RoleCond cond) {
+        return QueryBuilders.termQuery("roles.keyword", cond.getRoleKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final MemberCond cond) {
+        String memberKey;
+        try {
+            memberKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("members.keyword", memberKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final ResourceCond cond) {
+        return QueryBuilders.termQuery("resources.keyword", cond.getResourceKey());
+    }
+
+    private QueryBuilder fillAttrQuery(
+            final PlainSchema schema,
+            final PlainAttrValue attrValue,
+            final AttributeCond cond) {
+
+        String name = schema.getType() == AttrSchemaType.String
+                || schema.getType() == AttrSchemaType.Enum
+                ? schema.getKey() + ".keyword"
+                : schema.getKey();
+        Object value = schema.getType() == AttrSchemaType.Date && attrValue.getDateValue() != null
+                ? attrValue.getDateValue().getTime()
+                : attrValue.getValue();
+
+        QueryBuilder builder = EMPTY_QUERY_BUILDER;
+
+        switch (cond.getType()) {
+            case ISNOTNULL:
+                builder = QueryBuilders.existsQuery(name);
+                break;
+
+            case ISNULL:
+                builder = QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(name));
+                break;
+
+            case ILIKE:
+                builder = QueryBuilders.queryStringQuery(
+                        schema.getKey() + ":" + cond.getExpression().replace('%', '*'));
+                break;
+
+            case LIKE:
+                builder = QueryBuilders.wildcardQuery(name, cond.getExpression().replace('%', '*'));
+                break;
+
+            case IEQ:
+                builder = QueryBuilders.matchQuery(schema.getKey(), value);
+                break;
+
+            case EQ:
+                builder = QueryBuilders.termQuery(name, value);
+                break;
+
+            case GE:
+                builder = QueryBuilders.rangeQuery(name).gte(value);
+                break;
+
+            case GT:
+                builder = QueryBuilders.rangeQuery(name).gt(value);
+                break;
+
+            case LE:
+                builder = QueryBuilders.rangeQuery(name).lte(value);
+                break;
+
+            case LT:
+                builder = QueryBuilders.rangeQuery(name).lt(value);
+                break;
+
+            default:
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final AttributeCond cond, final AnyTypeKind kind) {
+        Pair<PlainSchema, PlainAttrValue> checked;
+        try {
+            checked = check(cond, kind);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return fillAttrQuery(checked.getLeft(), checked.getRight(), cond);
+    }
+
+    private QueryBuilder getQueryBuilder(final AnyCond cond, final AnyTypeKind kind) {
+        Triple<PlainSchema, PlainAttrValue, AnyCond> checked;
+        try {
+            checked = check(cond, kind);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return fillAttrQuery(checked.getLeft(), checked.getMiddle(), checked.getRight());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
new file mode 100644
index 0000000..6b8f4aa
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
@@ -0,0 +1,18 @@
+# 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.
+content.directory=${conf.directory}
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/pom.xml b/ext/elasticsearch/pom.xml
new file mode 100644
index 0000000..0bbb7b3
--- /dev/null
+++ b/ext/elasticsearch/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-ext</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch</name>
+  <description>Apache Syncope Ext: Elasticsearch</description>
+  <groupId>org.apache.syncope.ext</groupId>
+  <artifactId>syncope-ext-elasticsearch</artifactId>
+  <packaging>pom</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+  </properties>
+  
+  <modules>
+    <module>client-elasticsearch</module>
+    <module>persistence-jpa</module>
+    <module>provisioning-java</module>
+  </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/provisioning-java/pom.xml b/ext/elasticsearch/provisioning-java/pom.xml
new file mode 100644
index 0000000..7108905
--- /dev/null
+++ b/ext/elasticsearch/provisioning-java/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-elasticsearch</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch Provisioning Java</name>
+  <description>Apache Syncope Ext: Elasticsearch Provisioning Java</description>
+  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+  <artifactId>syncope-ext-elasticsearch-provisioning-java</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-provisioning-java</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+      <artifactId>syncope-ext-elasticsearch-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
new file mode 100644
index 0000000..b9d8b15
--- /dev/null
+++ b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
@@ -0,0 +1,137 @@
+/*
+ * 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.job;
+
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+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.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
+import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
+import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
+import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
+import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.client.Client;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Remove and rebuild all Elasticsearch indexes with information from existing users, groups and any objects.
+ */
+public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
+
+    @Autowired
+    private Client client;
+
+    @Autowired
+    private ElasticsearchUtils elasticsearchUtils;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnyObjectDAO anyObjectDAO;
+
+    @Override
+    protected String doExecute(final boolean dryRun) throws JobExecutionException {
+        if (!dryRun) {
+            try {
+                LOG.debug("Start rebuild index {}", AuthContextUtils.getDomain().toLowerCase());
+
+                IndicesExistsResponse existsIndexResponse = client.admin().indices().
+                        exists(new IndicesExistsRequest(AuthContextUtils.getDomain().toLowerCase())).
+                        get();
+                if (existsIndexResponse.isExists()) {
+                    DeleteIndexResponse deleteIndexResponse = client.admin().indices().
+                            delete(new DeleteIndexRequest(AuthContextUtils.getDomain().toLowerCase())).
+                            get();
+                    LOG.debug("Successfully removed {}: {}",
+                            AuthContextUtils.getDomain().toLowerCase(), deleteIndexResponse);
+                }
+
+                CreateIndexResponse createIndexResponse = client.admin().indices().
+                        create(new CreateIndexRequest(AuthContextUtils.getDomain().toLowerCase())).
+                        get();
+                LOG.debug("Successfully created {}: {}",
+                        AuthContextUtils.getDomain().toLowerCase(), createIndexResponse);
+
+                LOG.debug("Indexing users...");
+                for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (User user : userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.USER.name(),
+                                user.getKey()).
+                                setSource(elasticsearchUtils.builder(user)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", user, response);
+                    }
+                }
+                LOG.debug("Indexing groups...");
+                for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (Group group : groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.GROUP.name(),
+                                group.getKey()).
+                                setSource(elasticsearchUtils.builder(group)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", group, response);
+                    }
+                }
+                LOG.debug("Indexing any objects...");
+                for (int page = 1; page <= (anyObjectDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (AnyObject anyObject : anyObjectDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.ANY_OBJECT.name(),
+                                anyObject.getKey()).
+                                setSource(elasticsearchUtils.builder(anyObject)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", anyObject, response);
+                    }
+                }
+
+                LOG.debug("Rebuild index {} successfully completed", AuthContextUtils.getDomain().toLowerCase());
+            } catch (Exception e) {
+                throw new JobExecutionException(
+                        "While rebuilding index " + AuthContextUtils.getDomain().toLowerCase(), e);
+            }
+        }
+
+        return "SUCCESS";
+    }
+
+    @Override
+    protected boolean hasToBeRegistered(final TaskExec execution) {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/ext/pom.xml
----------------------------------------------------------------------
diff --git a/ext/pom.xml b/ext/pom.xml
index f832188..6806ed5 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -80,6 +80,7 @@ under the License.
     <module>camel</module>
     <module>swagger-ui</module>
     <module>saml2sp</module>
+    <module>elasticsearch</module>
   </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index b4fa2cd..58c9282 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -413,6 +413,70 @@ under the License.
     </profile>
   
     <profile>
+      <id>elasticsearch</id>
+      
+      <dependencies>
+        <dependency>
+          <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+          <artifactId>syncope-ext-elasticsearch-provisioning-java</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+          <artifactId>syncope-ext-elasticsearch-persistence-jpa</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+      </dependencies>
+      
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-war-plugin</artifactId>
+            <configuration>
+              <webResources>
+                <resource>
+                  <directory>src/main/webapp/WEB-INF</directory>
+                  <excludes>
+                    <exclude>web.xml</exclude>
+                  </excludes>
+                </resource>
+              </webResources>
+              <webXml>${basedir}/src/main/resources/elasticsearch/web.xml</webXml>
+            </configuration>
+          </plugin>
+        </plugins>
+        
+        <testResources>
+          <testResource>
+            <directory>src/test/resources</directory>
+            <filtering>true</filtering>
+            <excludes>
+              <exclude>keystore</exclude>
+            </excludes>
+          </testResource>
+          <testResource>
+            <directory>src/test/resources</directory>
+            <filtering>false</filtering>
+            <includes>
+              <include>keystore</include>
+            </includes>
+          </testResource>
+          <testResource>
+            <directory>${basedir}/../../core/rest-cxf/src/main/resources</directory>
+            <includes>
+              <include>errorMessages.properties</include>
+            </includes>
+          </testResource>
+          <testResource>
+            <directory>src/main/resources/elasticsearch</directory>
+            <filtering>true</filtering>
+          </testResource>
+        </testResources>
+      </build>
+    </profile>
+  
+    <profile>
       <id>postgres-it</id>
 
       <properties>
@@ -759,6 +823,14 @@ under the License.
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-war-plugin</artifactId>
             <configuration>
+              <webResources>
+                <resource>
+                  <directory>src/main/webapp/WEB-INF</directory>
+                  <excludes>
+                    <exclude>web.xml</exclude>
+                  </excludes>
+                </resource>
+              </webResources>
               <webXml>${basedir}/src/main/resources/jboss/web.xml</webXml>
               <packagingExcludes>WEB-INF/lib/syncope-*-persistence-jpa-${project.version}.jar</packagingExcludes>
             </configuration>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
index ae329b9..5951117 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import javax.sql.DataSource;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
@@ -32,14 +33,18 @@ import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.report.StaticReportletConf;
 import org.apache.syncope.common.lib.report.UserReportletConf;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.core.logic.TaskLogic;
 import org.apache.syncope.core.logic.report.AuditReportlet;
 import org.apache.syncope.core.logic.report.GroupReportlet;
 import org.apache.syncope.core.logic.report.ReconciliationReportlet;
 import org.apache.syncope.core.logic.report.StaticReportlet;
 import org.apache.syncope.core.logic.report.UserReportlet;
 import org.apache.syncope.core.migration.MigrationPullActions;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.core.persistence.jpa.attrvalue.validation.AlwaysTrueValidator;
@@ -55,6 +60,9 @@ import org.apache.syncope.core.provisioning.java.propagation.LDAPPasswordPropaga
 import org.apache.syncope.core.provisioning.java.pushpull.DBPasswordPullActions;
 import org.apache.syncope.core.provisioning.java.pushpull.LDAPMembershipPullActions;
 import org.apache.syncope.core.provisioning.java.pushpull.LDAPPasswordPullActions;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * Static implementation providing information about the integration test environment.
@@ -171,14 +179,42 @@ public class ITImplementationLookup implements ImplementationLookup {
         }
     };
 
+    @Autowired
+    private AnySearchDAO anySearchDAO;
+
+    @Autowired
+    private DomainsHolder domainsHolder;
+
+    @Autowired
+    private TaskLogic taskLogic;
+
     @Override
     public Integer getPriority() {
-        return 400;
+        return Integer.MAX_VALUE;
     }
 
     @Override
     public void load() {
-        // nothing to do
+        // in case the Elasticsearch extension is enabled, reinit a clean index for all available domains
+        if (AopUtils.getTargetClass(anySearchDAO).getName().contains("Elasticsearch")) {
+            for (Map.Entry<String, DataSource> entry : domainsHolder.getDomains().entrySet()) {
+                AuthContextUtils.execWithAuthContext(entry.getKey(), new AuthContextUtils.Executable<Void>() {
+
+                    @Override
+                    public Void exec() {
+                        SchedTaskTO task = new SchedTaskTO();
+                        task.setJobDelegateClassName(
+                                "org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex");
+                        task.setName("Elasticsearch Reindex");
+                        task = taskLogic.createSchedTask(task);
+
+                        taskLogic.execute(task.getKey(), null, false);
+
+                        return null;
+                    }
+                });
+            }
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
new file mode 100644
index 0000000..6b8f4aa
--- /dev/null
+++ b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
@@ -0,0 +1,18 @@
+# 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.
+content.directory=${conf.directory}
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/fit/core-reference/src/main/resources/elasticsearch/web.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/elasticsearch/web.xml b/fit/core-reference/src/main/resources/elasticsearch/web.xml
new file mode 100644
index 0000000..6d1ea80
--- /dev/null
+++ b/fit/core-reference/src/main/resources/elasticsearch/web.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+         version="3.1">
+
+  <display-name>Apache Syncope ${syncope.version} Core</display-name>
+
+  <context-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value>
+      classpath*:/coreContext.xml
+      classpath*:/elasticsearchClientContext.xml
+      classpath*:/securityContext.xml
+      classpath*:/logicContext.xml
+      classpath*:/restCXFContext.xml
+      classpath*:/persistenceContext.xml
+      classpath*:/provisioning*Context.xml
+      classpath*:/workflow*Context.xml
+    </param-value>
+  </context-param>
+
+  <listener>
+    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+  </listener>
+
+  <login-config>
+    <auth-method>CLIENT-CERT</auth-method>
+  </login-config>
+
+</web-app>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/fit/core-reference/src/main/resources/log4j2.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/log4j2.xml b/fit/core-reference/src/main/resources/log4j2.xml
index 44198f7..f484c35 100644
--- a/fit/core-reference/src/main/resources/log4j2.xml
+++ b/fit/core-reference/src/main/resources/log4j2.xml
@@ -176,7 +176,15 @@ under the License.
       <appender-ref ref="mainFile"/>
       <appender-ref ref="main"/>
     </asyncLogger>
-    
+    <asyncLogger name="org.elasticsearch" additivity="false" level="ERROR">
+      <appender-ref ref="mainFile"/>
+      <appender-ref ref="main"/>
+    </asyncLogger>
+    <asyncLogger name="io.netty" additivity="false" level="ERROR">
+      <appender-ref ref="mainFile"/>
+      <appender-ref ref="main"/>
+    </asyncLogger>
+
     <root level="INFO">
       <appender-ref ref="mainFile"/>
       <appender-ref ref="main"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/fit/core-reference/src/test/resources/rebel.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/rebel.xml b/fit/core-reference/src/test/resources/rebel.xml
index e5bb258..12d68ec 100644
--- a/fit/core-reference/src/test/resources/rebel.xml
+++ b/fit/core-reference/src/test/resources/rebel.xml
@@ -82,6 +82,15 @@ under the License.
     </dir>
     <dir name="${basedir}/../../ext/saml2sp/rest-cxf/target/classes">
     </dir>
+    <dir name="${basedir}/../../ext/elasticsearch/client-elasticsearch/target/classes">
+      <exclude name="elasticsearchClientContext.xml"/>
+    </dir>
+    <dir name="${basedir}/../../ext/elasticsearch/persistence-jpa/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/elasticsearch/provisioning-java/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/elasticsearch/workflow-java/target/classes">
+    </dir>
   </classpath>
 
   <web>

http://git-wip-us.apache.org/repos/asf/syncope/blob/02123044/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 1158700..5683419 100644
--- a/pom.xml
+++ b/pom.xml
@@ -379,6 +379,8 @@ under the License.
     <slf4j.version>1.7.25</slf4j.version>
     <opensaml.version>3.3.0</opensaml.version>
 
+    <elasticsearch.version>5.3.2</elasticsearch.version>
+
     <log4j.version>2.8.2</log4j.version>
     <disruptor.version>3.3.6</disruptor.version>
 
@@ -703,6 +705,12 @@ under the License.
       <!-- /OpenSAML -->
           
       <dependency>
+        <groupId>org.elasticsearch.client</groupId>
+        <artifactId>transport</artifactId>
+        <version>${elasticsearch.version}</version>
+      </dependency>
+
+      <dependency>
         <groupId>org.apache.openjpa</groupId>
         <artifactId>openjpa-jdbc</artifactId>
         <version>${openjpa.version}</version>
@@ -964,9 +972,17 @@ under the License.
         <version>${quartz.version}</version>
         <exclusions>
           <exclusion>
-            <groupId>c3p0</groupId>
+            <groupId>com.mchange</groupId>
             <artifactId>c3p0</artifactId>
           </exclusion>
+          <exclusion>
+            <groupId>com.mchange</groupId>
+            <artifactId>mchange-commons-java</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP-java6</artifactId>
+          </exclusion>
         </exclusions>
       </dependency>
       


[2/8] syncope git commit: [SYNCOPE-1077] Extension provided, not enabled by default in fit/core-reference (profile available)

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
new file mode 100644
index 0000000..572ef37
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
@@ -0,0 +1,194 @@
+/*
+ * 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.ext.elasticsearch.client;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
+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.persistence.api.entity.user.UDynGroupMembership;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.URelationship;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Utility methods for usage with Elasticsearch.
+ */
+public class ElasticsearchUtils {
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnyObjectDAO anyObjectDAO;
+
+    private int indexMaxResultWindow = 10000;
+
+    public void setIndexMaxResultWindow(final int indexMaxResultWindow) {
+        this.indexMaxResultWindow = indexMaxResultWindow;
+    }
+
+    public int getIndexMaxResultWindow() {
+        return indexMaxResultWindow;
+    }
+
+    /**
+     * Returns the builder specialized with content from the provided any.
+     *
+     * @param any user, group or any object to index
+     * @return builder specialized with content from the provided any
+     * @throws IOException in case of errors
+     */
+    @Transactional
+    public XContentBuilder builder(final Any<?> any) throws IOException {
+        XContentBuilder builder = XContentFactory.jsonBuilder().
+                startObject().
+                field("id", any.getKey()).
+                field("realm", any.getRealm().getFullPath()).
+                field("anyType", any.getType().getKey()).
+                field("creationDate", any.getCreationDate()).
+                field("creator", any.getCreator()).
+                field("lastChangeDate", any.getLastChangeDate()).
+                field("lastModified", any.getLastModifier()).
+                field("status", any.getStatus()).
+                field("resources",
+                        any instanceof User
+                                ? userDAO.findAllResourceKeys(any.getKey())
+                                : any instanceof AnyObject
+                                        ? anyObjectDAO.findAllResourceKeys(any.getKey())
+                                        : any.getResourceKeys());
+
+        if (any instanceof AnyObject) {
+            AnyObject anyObject = ((AnyObject) any);
+            builder = builder.field("name", anyObject.getName());
+
+            List<Object> memberships = new ArrayList<Object>(anyObjectDAO.findAllGroupKeys(anyObject));
+            builder = builder.field("memberships", memberships);
+
+            List<Object> relationships = new ArrayList<>();
+            List<Object> relationshipTypes = new ArrayList<>();
+            for (ARelationship relationship : anyObjectDAO.findAllARelationships(anyObject)) {
+                relationships.add(relationship.getRightEnd().getKey());
+                relationshipTypes.add(relationship.getType().getKey());
+            }
+            builder = builder.field("relationships", relationships);
+            builder = builder.field("relationshipTypes", relationshipTypes);
+        } else if (any instanceof Group) {
+            Group group = ((Group) any);
+            builder = builder.field("name", group.getName());
+            if (group.getUserOwner() != null) {
+                builder = builder.field("userOwner", group.getUserOwner().getKey());
+            }
+            if (group.getGroupOwner() != null) {
+                builder = builder.field("groupOwner", group.getGroupOwner().getKey());
+            }
+
+            List<Object> members = CollectionUtils.collect(groupDAO.findUMemberships(group),
+                    new Transformer<UMembership, Object>() {
+
+                @Override
+                public Object transform(final UMembership input) {
+                    return input.getLeftEnd().getKey();
+                }
+            }, new ArrayList<>());
+            UDynGroupMembership udynmembership = group.getUDynMembership();
+            if (udynmembership != null) {
+                CollectionUtils.collect(udynmembership.getMembers(), EntityUtils.keyTransformer(), members);
+            }
+            CollectionUtils.collect(groupDAO.findAMemberships(group),
+                    new Transformer<AMembership, Object>() {
+
+                @Override
+                public Object transform(final AMembership input) {
+                    return input.getLeftEnd().getKey();
+                }
+            }, members);
+            for (ADynGroupMembership adynmembership : group.getADynMemberships()) {
+                CollectionUtils.collect(adynmembership.getMembers(), EntityUtils.keyTransformer(), members);
+            }
+            builder = builder.field("members", members);
+        } else if (any instanceof User) {
+            User user = ((User) any);
+            builder = builder.
+                    field("username", user.getUsername()).
+                    field("lastLoginDate", user.getLastLoginDate()).
+                    field("lastRecertification", user.getLastRecertification()).
+                    field("lastRecertificator", user.getLastRecertificator());
+
+            List<Object> roles = CollectionUtils.collect(userDAO.findAllRoles(user),
+                    EntityUtils.<Role>keyTransformer(), new ArrayList<>());
+            builder = builder.field("roles", roles);
+
+            List<Object> memberships = new ArrayList<Object>(userDAO.findAllGroupKeys(user));
+            builder = builder.field("memberships", memberships);
+
+            List<Object> relationships = new ArrayList<>();
+            Set<Object> relationshipTypes = new HashSet<>();
+            for (URelationship relationship : user.getRelationships()) {
+                relationships.add(relationship.getRightEnd().getKey());
+                relationshipTypes.add(relationship.getType().getKey());
+            }
+            builder = builder.field("relationships", relationships);
+            builder = builder.field("relationshipTypes", relationshipTypes);
+        }
+
+        for (PlainAttr<?> plainAttr : any.getPlainAttrs()) {
+            List<Object> values = CollectionUtils.collect(plainAttr.getValues(),
+                    new Transformer<PlainAttrValue, Object>() {
+
+                @Override
+                public Object transform(final PlainAttrValue input) {
+                    return input.getValue();
+                }
+            }, new ArrayList<>(plainAttr.getValues().size()));
+            if (plainAttr.getUniqueValue() != null) {
+                values.add(plainAttr.getUniqueValue().getValue());
+            }
+
+            builder = builder.field(plainAttr.getSchema().getKey(), values);
+        }
+
+        builder = builder.endObject();
+
+        return builder;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml b/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
new file mode 100644
index 0000000..f870317
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchClientFactoryBean">
+    <property name="settings">
+      <map>
+        <entry key="cluster.name">
+          <value>elasticsearch</value>
+        </entry>
+        <entry key="client.transport.sniff">
+          <value>true</value>
+        </entry>
+      </map>
+    </property>  
+    <property name="addresses">
+      <map>
+        <entry key="localhost">
+          <value>9300</value>
+        </entry>
+      </map>
+    </property>
+  </bean>
+  
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils">
+    <property name="indexMaxResultWindow" value="10000"/>
+  </bean>
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager"/>
+  
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/pom.xml b/ext/elasticsearch/persistence-jpa/pom.xml
new file mode 100644
index 0000000..129b36b
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-elasticsearch</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch Persistence JPA</name>
+  <description>Apache Syncope Ext: Elasticsearch Persistence JPA</description>
+  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+  <artifactId>syncope-ext-elasticsearch-persistence-jpa</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>    
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-persistence-jpa</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+      <artifactId>syncope-ext-elasticsearch-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
new file mode 100644
index 0000000..34651e1
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
@@ -0,0 +1,355 @@
+/*
+ * 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.persistence.jpa.dao;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
+import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.ResourceCond;
+import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchType;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.index.query.DisMaxQueryBuilder;
+import org.elasticsearch.index.query.MatchNoneQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Search engine implementation for users, groups and any objects, based on Elasticsearch.
+ */
+public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
+
+    private static final QueryBuilder EMPTY_QUERY_BUILDER = new MatchNoneQueryBuilder();
+
+    @Autowired
+    private Client client;
+
+    @Autowired
+    private ElasticsearchUtils elasticsearchUtils;
+
+    private DisMaxQueryBuilder adminRealmsFilter(final Set<String> adminRealms) {
+        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
+
+        for (String realmPath : RealmUtils.normalize(adminRealms)) {
+            Realm realm = realmDAO.findByFullPath(realmPath);
+            if (realm == null) {
+                LOG.warn("Ignoring invalid realm {}", realmPath);
+            } else {
+                for (Realm descendant : realmDAO.findDescendants(realm)) {
+                    builder.add(QueryBuilders.termQuery("realm.keyword", descendant.getFullPath()));
+                }
+            }
+        }
+
+        return builder;
+    }
+
+    private SearchRequestBuilder searchRequestBuilder(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final AnyTypeKind kind) {
+
+        return client.prepareSearch(AuthContextUtils.getDomain().toLowerCase()).
+                setTypes(kind.name()).
+                setSearchType(SearchType.QUERY_THEN_FETCH).
+                setQuery(SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms)
+                        ? getQueryBuilder(cond, kind)
+                        : QueryBuilders.boolQuery().
+                                must(adminRealmsFilter(adminRealms)).
+                                must(getQueryBuilder(cond, kind)));
+    }
+
+    @Override
+    protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
+        SearchRequestBuilder builder = searchRequestBuilder(adminRealms, cond, kind).
+                setFrom(0).setSize(0);
+
+        return (int) builder.get().getHits().getTotalHits();
+    }
+
+    @Override
+    protected <T extends Any<?>> List<T> doSearch(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final int page,
+            final int itemsPerPage,
+            final List<OrderByClause> orderBy,
+            final AnyTypeKind kind) {
+
+        SearchRequestBuilder builder = searchRequestBuilder(adminRealms, cond, kind).
+                setFrom(page <= 0 ? 0 : page - 1).
+                setSize(itemsPerPage < 0 ? elasticsearchUtils.getIndexMaxResultWindow() : itemsPerPage);
+
+        return buildResult(
+                CollectionUtils.collect(Arrays.asList(builder.get().getHits().getHits()),
+                        new Transformer<SearchHit, Object>() {
+
+                    @Override
+                    public Object transform(final SearchHit input) {
+                        return input.getId();
+                    }
+                }, new ArrayList<>()),
+                kind);
+    }
+
+    private QueryBuilder getQueryBuilder(final SearchCond cond, final AnyTypeKind kind) {
+        QueryBuilder builder = EMPTY_QUERY_BUILDER;
+
+        switch (cond.getType()) {
+            case LEAF:
+            case NOT_LEAF:
+                if (cond.getAnyTypeCond() != null && AnyTypeKind.ANY_OBJECT == kind) {
+                    builder = getQueryBuilder(cond.getAnyTypeCond());
+                } else if (cond.getRelationshipTypeCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getRelationshipTypeCond());
+                } else if (cond.getRelationshipCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getRelationshipCond());
+                } else if (cond.getMembershipCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getMembershipCond());
+                } else if (cond.getAssignableCond() != null) {
+                    builder = getQueryBuilder(cond.getAssignableCond());
+                } else if (cond.getRoleCond() != null && AnyTypeKind.USER == kind) {
+                    builder = getQueryBuilder(cond.getRoleCond());
+                } else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == kind) {
+                    builder = getQueryBuilder(cond.getMemberCond());
+                } else if (cond.getResourceCond() != null) {
+                    builder = getQueryBuilder(cond.getResourceCond());
+                } else if (cond.getAttributeCond() != null) {
+                    builder = getQueryBuilder(cond.getAttributeCond(), kind);
+                } else if (cond.getAnyCond() != null) {
+                    builder = getQueryBuilder(cond.getAnyCond(), kind);
+                }
+                builder = checkNot(builder, cond.getType() == SearchCond.Type.NOT_LEAF);
+                break;
+
+            case AND:
+                builder = QueryBuilders.boolQuery().
+                        must(getQueryBuilder(cond.getLeftSearchCond(), kind)).
+                        must(getQueryBuilder(cond.getRightSearchCond(), kind));
+                break;
+
+            case OR:
+                builder = QueryBuilders.disMaxQuery().
+                        add(getQueryBuilder(cond.getLeftSearchCond(), kind)).
+                        add(getQueryBuilder(cond.getRightSearchCond(), kind));
+                break;
+
+            default:
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder checkNot(final QueryBuilder builder, final boolean not) {
+        return not
+                ? QueryBuilders.boolQuery().mustNot(builder)
+                : builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final AnyTypeCond cond) {
+        return QueryBuilders.termQuery("anyType.keyword", cond.getAnyTypeKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final RelationshipTypeCond cond) {
+        return QueryBuilders.termQuery("relationshipTypes.keyword", cond.getRelationshipTypeKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final RelationshipCond cond) {
+        String rightAnyObjectKey;
+        try {
+            rightAnyObjectKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("relationships.keyword", rightAnyObjectKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final MembershipCond cond) {
+        String groupKey;
+        try {
+            groupKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("memberships.keyword", groupKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final AssignableCond cond) {
+        Realm realm;
+        try {
+            realm = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
+        if (cond.isFromGroup()) {
+            for (Realm current = realm; current.getParent() != null; current = current.getParent()) {
+                builder.add(QueryBuilders.termQuery("realm.keyword", current.getFullPath()));
+            }
+            builder.add(QueryBuilders.termQuery("realm.keyword", realmDAO.getRoot().getFullPath()));
+        } else {
+            for (Realm current : realmDAO.findDescendants(realm)) {
+                builder.add(QueryBuilders.termQuery("realm.keyword", current.getFullPath()));
+            }
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final RoleCond cond) {
+        return QueryBuilders.termQuery("roles.keyword", cond.getRoleKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final MemberCond cond) {
+        String memberKey;
+        try {
+            memberKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("members.keyword", memberKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final ResourceCond cond) {
+        return QueryBuilders.termQuery("resources.keyword", cond.getResourceKey());
+    }
+
+    private QueryBuilder fillAttrQuery(
+            final PlainSchema schema,
+            final PlainAttrValue attrValue,
+            final AttributeCond cond) {
+
+        String name = schema.getType() == AttrSchemaType.String
+                || schema.getType() == AttrSchemaType.Enum
+                ? schema.getKey() + ".keyword"
+                : schema.getKey();
+        Object value = schema.getType() == AttrSchemaType.Date && attrValue.getDateValue() != null
+                ? attrValue.getDateValue().getTime()
+                : attrValue.getValue();
+
+        QueryBuilder builder = EMPTY_QUERY_BUILDER;
+
+        switch (cond.getType()) {
+            case ISNOTNULL:
+                builder = QueryBuilders.existsQuery(name);
+                break;
+
+            case ISNULL:
+                builder = QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(name));
+                break;
+
+            case ILIKE:
+                builder = QueryBuilders.queryStringQuery(
+                        schema.getKey() + ":" + cond.getExpression().replace('%', '*'));
+                break;
+
+            case LIKE:
+                builder = QueryBuilders.wildcardQuery(name, cond.getExpression().replace('%', '*'));
+                break;
+
+            case IEQ:
+                builder = QueryBuilders.matchQuery(schema.getKey(), value);
+                break;
+
+            case EQ:
+                builder = QueryBuilders.termQuery(name, value);
+                break;
+
+            case GE:
+                builder = QueryBuilders.rangeQuery(name).gte(value);
+                break;
+
+            case GT:
+                builder = QueryBuilders.rangeQuery(name).gt(value);
+                break;
+
+            case LE:
+                builder = QueryBuilders.rangeQuery(name).lte(value);
+                break;
+
+            case LT:
+                builder = QueryBuilders.rangeQuery(name).lt(value);
+                break;
+
+            default:
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final AttributeCond cond, final AnyTypeKind kind) {
+        Pair<PlainSchema, PlainAttrValue> checked;
+        try {
+            checked = check(cond, kind);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return fillAttrQuery(checked.getLeft(), checked.getRight(), cond);
+    }
+
+    private QueryBuilder getQueryBuilder(final AnyCond cond, final AnyTypeKind kind) {
+        Triple<PlainSchema, PlainAttrValue, AnyCond> checked;
+        try {
+            checked = check(cond, kind);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return fillAttrQuery(checked.getLeft(), checked.getMiddle(), checked.getRight());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
new file mode 100644
index 0000000..6b8f4aa
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
@@ -0,0 +1,18 @@
+# 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.
+content.directory=${conf.directory}
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/pom.xml b/ext/elasticsearch/pom.xml
new file mode 100644
index 0000000..0bbb7b3
--- /dev/null
+++ b/ext/elasticsearch/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-ext</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch</name>
+  <description>Apache Syncope Ext: Elasticsearch</description>
+  <groupId>org.apache.syncope.ext</groupId>
+  <artifactId>syncope-ext-elasticsearch</artifactId>
+  <packaging>pom</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+  </properties>
+  
+  <modules>
+    <module>client-elasticsearch</module>
+    <module>persistence-jpa</module>
+    <module>provisioning-java</module>
+  </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/provisioning-java/pom.xml b/ext/elasticsearch/provisioning-java/pom.xml
new file mode 100644
index 0000000..7108905
--- /dev/null
+++ b/ext/elasticsearch/provisioning-java/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-elasticsearch</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch Provisioning Java</name>
+  <description>Apache Syncope Ext: Elasticsearch Provisioning Java</description>
+  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+  <artifactId>syncope-ext-elasticsearch-provisioning-java</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-provisioning-java</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+      <artifactId>syncope-ext-elasticsearch-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
new file mode 100644
index 0000000..b9d8b15
--- /dev/null
+++ b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
@@ -0,0 +1,137 @@
+/*
+ * 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.job;
+
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+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.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
+import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
+import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
+import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
+import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.client.Client;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Remove and rebuild all Elasticsearch indexes with information from existing users, groups and any objects.
+ */
+public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
+
+    @Autowired
+    private Client client;
+
+    @Autowired
+    private ElasticsearchUtils elasticsearchUtils;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnyObjectDAO anyObjectDAO;
+
+    @Override
+    protected String doExecute(final boolean dryRun) throws JobExecutionException {
+        if (!dryRun) {
+            try {
+                LOG.debug("Start rebuild index {}", AuthContextUtils.getDomain().toLowerCase());
+
+                IndicesExistsResponse existsIndexResponse = client.admin().indices().
+                        exists(new IndicesExistsRequest(AuthContextUtils.getDomain().toLowerCase())).
+                        get();
+                if (existsIndexResponse.isExists()) {
+                    DeleteIndexResponse deleteIndexResponse = client.admin().indices().
+                            delete(new DeleteIndexRequest(AuthContextUtils.getDomain().toLowerCase())).
+                            get();
+                    LOG.debug("Successfully removed {}: {}",
+                            AuthContextUtils.getDomain().toLowerCase(), deleteIndexResponse);
+                }
+
+                CreateIndexResponse createIndexResponse = client.admin().indices().
+                        create(new CreateIndexRequest(AuthContextUtils.getDomain().toLowerCase())).
+                        get();
+                LOG.debug("Successfully created {}: {}",
+                        AuthContextUtils.getDomain().toLowerCase(), createIndexResponse);
+
+                LOG.debug("Indexing users...");
+                for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (User user : userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.USER.name(),
+                                user.getKey()).
+                                setSource(elasticsearchUtils.builder(user)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", user, response);
+                    }
+                }
+                LOG.debug("Indexing groups...");
+                for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (Group group : groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.GROUP.name(),
+                                group.getKey()).
+                                setSource(elasticsearchUtils.builder(group)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", group, response);
+                    }
+                }
+                LOG.debug("Indexing any objects...");
+                for (int page = 1; page <= (anyObjectDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (AnyObject anyObject : anyObjectDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.ANY_OBJECT.name(),
+                                anyObject.getKey()).
+                                setSource(elasticsearchUtils.builder(anyObject)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", anyObject, response);
+                    }
+                }
+
+                LOG.debug("Rebuild index {} successfully completed", AuthContextUtils.getDomain().toLowerCase());
+            } catch (Exception e) {
+                throw new JobExecutionException(
+                        "While rebuilding index " + AuthContextUtils.getDomain().toLowerCase(), e);
+            }
+        }
+
+        return "SUCCESS";
+    }
+
+    @Override
+    protected boolean hasToBeRegistered(final TaskExec execution) {
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/pom.xml
----------------------------------------------------------------------
diff --git a/ext/pom.xml b/ext/pom.xml
index 591194f..c5ede76 100644
--- a/ext/pom.xml
+++ b/ext/pom.xml
@@ -80,6 +80,7 @@ under the License.
     <module>camel</module>
     <module>swagger-ui</module>
     <module>saml2sp</module>
+    <module>elasticsearch</module>
   </modules>
 
 </project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/fit/core-reference/pom.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 0b620a9..f6888a5 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -413,6 +413,70 @@ under the License.
     </profile>
   
     <profile>
+      <id>elasticsearch</id>
+      
+      <dependencies>
+        <dependency>
+          <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+          <artifactId>syncope-ext-elasticsearch-provisioning-java</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+        <dependency>
+          <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+          <artifactId>syncope-ext-elasticsearch-persistence-jpa</artifactId>
+          <version>${project.version}</version>
+        </dependency>
+      </dependencies>
+      
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-war-plugin</artifactId>
+            <configuration>
+              <webResources>
+                <resource>
+                  <directory>src/main/webapp/WEB-INF</directory>
+                  <excludes>
+                    <exclude>web.xml</exclude>
+                  </excludes>
+                </resource>
+              </webResources>
+              <webXml>${basedir}/src/main/resources/elasticsearch/web.xml</webXml>
+            </configuration>
+          </plugin>
+        </plugins>
+        
+        <testResources>
+          <testResource>
+            <directory>src/test/resources</directory>
+            <filtering>true</filtering>
+            <excludes>
+              <exclude>keystore</exclude>
+            </excludes>
+          </testResource>
+          <testResource>
+            <directory>src/test/resources</directory>
+            <filtering>false</filtering>
+            <includes>
+              <include>keystore</include>
+            </includes>
+          </testResource>
+          <testResource>
+            <directory>${basedir}/../../core/rest-cxf/src/main/resources</directory>
+            <includes>
+              <include>errorMessages.properties</include>
+            </includes>
+          </testResource>
+          <testResource>
+            <directory>src/main/resources/elasticsearch</directory>
+            <filtering>true</filtering>
+          </testResource>
+        </testResources>
+      </build>
+    </profile>
+  
+    <profile>
       <id>postgres-it</id>
 
       <properties>
@@ -759,6 +823,14 @@ under the License.
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-war-plugin</artifactId>
             <configuration>
+              <webResources>
+                <resource>
+                  <directory>src/main/webapp/WEB-INF</directory>
+                  <excludes>
+                    <exclude>web.xml</exclude>
+                  </excludes>
+                </resource>
+              </webResources>
               <webXml>${basedir}/src/main/resources/jboss/web.xml</webXml>
               <packagingExcludes>WEB-INF/lib/syncope-*-persistence-jpa-${project.version}.jar</packagingExcludes>
             </configuration>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
index ae329b9..5951117 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import javax.sql.DataSource;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
@@ -32,14 +33,18 @@ import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.report.StaticReportletConf;
 import org.apache.syncope.common.lib.report.UserReportletConf;
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.core.logic.TaskLogic;
 import org.apache.syncope.core.logic.report.AuditReportlet;
 import org.apache.syncope.core.logic.report.GroupReportlet;
 import org.apache.syncope.core.logic.report.ReconciliationReportlet;
 import org.apache.syncope.core.logic.report.StaticReportlet;
 import org.apache.syncope.core.logic.report.UserReportlet;
 import org.apache.syncope.core.migration.MigrationPullActions;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.core.persistence.jpa.attrvalue.validation.AlwaysTrueValidator;
@@ -55,6 +60,9 @@ import org.apache.syncope.core.provisioning.java.propagation.LDAPPasswordPropaga
 import org.apache.syncope.core.provisioning.java.pushpull.DBPasswordPullActions;
 import org.apache.syncope.core.provisioning.java.pushpull.LDAPMembershipPullActions;
 import org.apache.syncope.core.provisioning.java.pushpull.LDAPPasswordPullActions;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 
 /**
  * Static implementation providing information about the integration test environment.
@@ -171,14 +179,42 @@ public class ITImplementationLookup implements ImplementationLookup {
         }
     };
 
+    @Autowired
+    private AnySearchDAO anySearchDAO;
+
+    @Autowired
+    private DomainsHolder domainsHolder;
+
+    @Autowired
+    private TaskLogic taskLogic;
+
     @Override
     public Integer getPriority() {
-        return 400;
+        return Integer.MAX_VALUE;
     }
 
     @Override
     public void load() {
-        // nothing to do
+        // in case the Elasticsearch extension is enabled, reinit a clean index for all available domains
+        if (AopUtils.getTargetClass(anySearchDAO).getName().contains("Elasticsearch")) {
+            for (Map.Entry<String, DataSource> entry : domainsHolder.getDomains().entrySet()) {
+                AuthContextUtils.execWithAuthContext(entry.getKey(), new AuthContextUtils.Executable<Void>() {
+
+                    @Override
+                    public Void exec() {
+                        SchedTaskTO task = new SchedTaskTO();
+                        task.setJobDelegateClassName(
+                                "org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex");
+                        task.setName("Elasticsearch Reindex");
+                        task = taskLogic.createSchedTask(task);
+
+                        taskLogic.execute(task.getKey(), null, false);
+
+                        return null;
+                    }
+                });
+            }
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
new file mode 100644
index 0000000..6b8f4aa
--- /dev/null
+++ b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
@@ -0,0 +1,18 @@
+# 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.
+content.directory=${conf.directory}
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/fit/core-reference/src/main/resources/elasticsearch/web.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/elasticsearch/web.xml b/fit/core-reference/src/main/resources/elasticsearch/web.xml
new file mode 100644
index 0000000..6d1ea80
--- /dev/null
+++ b/fit/core-reference/src/main/resources/elasticsearch/web.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+         version="3.1">
+
+  <display-name>Apache Syncope ${syncope.version} Core</display-name>
+
+  <context-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value>
+      classpath*:/coreContext.xml
+      classpath*:/elasticsearchClientContext.xml
+      classpath*:/securityContext.xml
+      classpath*:/logicContext.xml
+      classpath*:/restCXFContext.xml
+      classpath*:/persistenceContext.xml
+      classpath*:/provisioning*Context.xml
+      classpath*:/workflow*Context.xml
+    </param-value>
+  </context-param>
+
+  <listener>
+    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+  </listener>
+
+  <login-config>
+    <auth-method>CLIENT-CERT</auth-method>
+  </login-config>
+
+</web-app>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/fit/core-reference/src/main/resources/log4j2.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/log4j2.xml b/fit/core-reference/src/main/resources/log4j2.xml
index 44198f7..f484c35 100644
--- a/fit/core-reference/src/main/resources/log4j2.xml
+++ b/fit/core-reference/src/main/resources/log4j2.xml
@@ -176,7 +176,15 @@ under the License.
       <appender-ref ref="mainFile"/>
       <appender-ref ref="main"/>
     </asyncLogger>
-    
+    <asyncLogger name="org.elasticsearch" additivity="false" level="ERROR">
+      <appender-ref ref="mainFile"/>
+      <appender-ref ref="main"/>
+    </asyncLogger>
+    <asyncLogger name="io.netty" additivity="false" level="ERROR">
+      <appender-ref ref="mainFile"/>
+      <appender-ref ref="main"/>
+    </asyncLogger>
+
     <root level="INFO">
       <appender-ref ref="mainFile"/>
       <appender-ref ref="main"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/fit/core-reference/src/test/resources/rebel.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/rebel.xml b/fit/core-reference/src/test/resources/rebel.xml
index e5bb258..12d68ec 100644
--- a/fit/core-reference/src/test/resources/rebel.xml
+++ b/fit/core-reference/src/test/resources/rebel.xml
@@ -82,6 +82,15 @@ under the License.
     </dir>
     <dir name="${basedir}/../../ext/saml2sp/rest-cxf/target/classes">
     </dir>
+    <dir name="${basedir}/../../ext/elasticsearch/client-elasticsearch/target/classes">
+      <exclude name="elasticsearchClientContext.xml"/>
+    </dir>
+    <dir name="${basedir}/../../ext/elasticsearch/persistence-jpa/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/elasticsearch/provisioning-java/target/classes">
+    </dir>
+    <dir name="${basedir}/../../ext/elasticsearch/workflow-java/target/classes">
+    </dir>
   </classpath>
 
   <web>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 5891e71..99549cc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -380,6 +380,8 @@ under the License.
     <slf4j.version>1.7.25</slf4j.version>
     <opensaml.version>3.3.0</opensaml.version>
 
+    <elasticsearch.version>5.3.2</elasticsearch.version>
+
     <log4j.version>2.8.2</log4j.version>
     <disruptor.version>3.3.6</disruptor.version>
 
@@ -710,6 +712,12 @@ under the License.
       <!-- /OpenSAML -->
           
       <dependency>
+        <groupId>org.elasticsearch.client</groupId>
+        <artifactId>transport</artifactId>
+        <version>${elasticsearch.version}</version>
+      </dependency>
+
+      <dependency>
         <groupId>org.apache.openjpa</groupId>
         <artifactId>openjpa-jdbc</artifactId>
         <version>${openjpa.version}</version>
@@ -971,9 +979,17 @@ under the License.
         <version>${quartz.version}</version>
         <exclusions>
           <exclusion>
-            <groupId>c3p0</groupId>
+            <groupId>com.mchange</groupId>
             <artifactId>c3p0</artifactId>
           </exclusion>
+          <exclusion>
+            <groupId>com.mchange</groupId>
+            <artifactId>mchange-commons-java</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>com.zaxxer</groupId>
+            <artifactId>HikariCP-java6</artifactId>
+          </exclusion>
         </exclusions>
       </dependency>
       


[5/8] syncope git commit: White noise

Posted by il...@apache.org.
White noise


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/9e09aa12
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/9e09aa12
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/9e09aa12

Branch: refs/heads/master
Commit: 9e09aa1288e78b68cd48c60f26af93d7c427b136
Parents: 2e7e2ce
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue May 2 17:37:55 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon May 8 13:32:48 2017 +0200

----------------------------------------------------------------------
 .../core/provisioning/camel/component/PropagateComponent.java | 1 +
 .../core/provisioning/camel/producer/CreateProducer.java      | 7 +------
 .../core/provisioning/camel/producer/UpdateProducer.java      | 7 +------
 3 files changed, 3 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/9e09aa12/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java
index b8a118a..f443c1d 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/component/PropagateComponent.java
@@ -58,6 +58,7 @@ public class PropagateComponent extends UriEndpointComponent {
         super(PropagateEndpoint.class);
     }
 
+    @Override
     protected Endpoint createEndpoint(final String uri, final String remaining,
             final Map<String, Object> parameters) throws Exception {
         PropagateType type = PropagateType.valueOf(remaining);

http://git-wip-us.apache.org/repos/asf/syncope/blob/9e09aa12/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
index 54ff11b..8193e3e 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/CreateProducer.java
@@ -70,11 +70,6 @@ public class CreateProducer extends AbstractProducer {
             } else if (actual instanceof AnyTO) {
                 WorkflowResult<String> created = (WorkflowResult<String>) exchange.getIn().getBody();
 
-                AnyTypeKind anyTypeKind = AnyTypeKind.GROUP;
-                if (actual instanceof AnyObjectTO) {
-                    anyTypeKind = AnyTypeKind.ANY_OBJECT;
-                }
-
                 if (actual instanceof GroupTO && isPull()) {
                     Map<String, String> groupOwnerMap = exchange.getProperty("groupOwnerMap", Map.class);
                     AttrTO groupOwner = ((GroupTO) actual).getPlainAttrMap().get(StringUtils.EMPTY);
@@ -93,7 +88,7 @@ public class CreateProducer extends AbstractProducer {
                     exchange.getOut().setBody(new ImmutablePair<>(created.getResult(), null));
                 } else {
                     List<PropagationTask> tasks = getPropagationManager().getCreateTasks(
-                            anyTypeKind,
+                            actual instanceof AnyObjectTO ? AnyTypeKind.ANY_OBJECT : AnyTypeKind.GROUP,
                             created.getResult(),
                             created.getPropByRes(),
                             ((AnyTO) actual).getVirAttrs(),

http://git-wip-us.apache.org/repos/asf/syncope/blob/9e09aa12/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
index 7c3761e..68fbd0e 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/UpdateProducer.java
@@ -66,13 +66,8 @@ public class UpdateProducer extends AbstractProducer {
             } else if (actual instanceof AnyPatch) {
                 WorkflowResult<String> updated = (WorkflowResult<String>) exchange.getIn().getBody();
 
-                AnyTypeKind anyTypeKind = AnyTypeKind.GROUP;
-                if (actual instanceof AnyObjectPatch) {
-                    anyTypeKind = AnyTypeKind.ANY_OBJECT;
-                }
-
                 List<PropagationTask> tasks = getPropagationManager().getUpdateTasks(
-                        anyTypeKind,
+                        actual instanceof AnyObjectPatch ? AnyTypeKind.ANY_OBJECT : AnyTypeKind.GROUP,
                         updated.getResult(),
                         false,
                         null,


[3/8] syncope git commit: [SYNCOPE-1077] Extension provided, not enabled by default in fit/core-reference (profile available)

Posted by il...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
index 15a9d3f..54423b8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAGroupDAO.java
@@ -40,6 +40,7 @@ import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 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.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
@@ -59,7 +60,11 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.event.AnyDeletedEvent;
+import org.springframework.aop.support.AopUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -69,9 +74,11 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Autowired
     private PlainAttrDAO plainAttrDAO;
 
+    private UserDAO userDAO;
+
     private AnyObjectDAO anyObjectDAO;
 
-    private UserDAO userDAO;
+    private AnySearchDAO jpaAnySearchDAO;
 
     private UserDAO userDAO() {
         synchronized (this) {
@@ -91,6 +98,20 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
         return anyObjectDAO;
     }
 
+    private AnySearchDAO jpaAnySearchDAO() {
+        synchronized (this) {
+            if (jpaAnySearchDAO == null) {
+                if (AopUtils.getTargetClass(searchDAO()).equals(JPAAnySearchDAO.class)) {
+                    jpaAnySearchDAO = searchDAO();
+                } else {
+                    jpaAnySearchDAO = (AnySearchDAO) ApplicationContextProvider.getBeanFactory().
+                            createBean(JPAAnySearchDAO.class, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+                }
+            }
+        }
+        return jpaAnySearchDAO;
+    }
+
     @Override
     protected AnyUtils init() {
         return new JPAAnyUtilsFactory().getInstance(AnyTypeKind.GROUP);
@@ -204,8 +225,8 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public List<AMembership> findAMemberships(final Group group) {
         TypedQuery<AMembership> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAAMembership.class.getSimpleName()
-                + " e WHERE e.rightEnd=:group", AMembership.class);
+                "SELECT e FROM " + JPAAMembership.class.getSimpleName() + " e WHERE e.rightEnd=:group",
+                AMembership.class);
         query.setParameter("group", group);
 
         return query.getResultList();
@@ -214,13 +235,23 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public List<UMembership> findUMemberships(final Group group) {
         TypedQuery<UMembership> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAUMembership.class.getSimpleName()
-                + " e WHERE e.rightEnd=:group", UMembership.class);
+                "SELECT e FROM " + JPAUMembership.class.getSimpleName() + " e WHERE e.rightEnd=:group",
+                UMembership.class);
         query.setParameter("group", group);
 
         return query.getResultList();
     }
 
+    @Override
+    public List<Group> findAll(final int page, final int itemsPerPage) {
+        TypedQuery<Group> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAGroup.class.getSimpleName() + " e", Group.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
+
+        return query.getResultList();
+    }
+
     private SearchCond buildDynMembershipCond(final String baseCondFIQL, final Realm groupRealm) {
         AssignableCond cond = new AssignableCond();
         cond.setRealmFullPath(groupRealm.getFullPath());
@@ -232,6 +263,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
     @Override
     public Group save(final Group group) {
         Group merged = super.save(group);
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
 
         // refresh dynaminc memberships
         if (merged.getUDynMembership() != null) {
@@ -242,6 +274,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             merged.getUDynMembership().getMembers().clear();
             for (User user : matching) {
                 merged.getUDynMembership().add(user);
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
             }
         }
         for (ADynGroupMembership memb : merged.getADynMemberships()) {
@@ -252,6 +285,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             memb.getMembers().clear();
             for (AnyObject anyObject : matching) {
                 memb.add(anyObject);
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, anyObject));
             }
         }
 
@@ -272,6 +306,7 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             }
 
             anyObjectDAO().save(leftEnd);
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd));
         }
         for (UMembership membership : findUMemberships(group)) {
             User leftEnd = membership.getLeftEnd();
@@ -285,9 +320,11 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
             }
 
             userDAO().save(leftEnd);
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, leftEnd));
         }
 
         entityManager().remove(group);
+        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.GROUP, group.getKey()));
     }
 
     @Override
@@ -300,35 +337,74 @@ public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
         return query.getResultList();
     }
 
+    private List<Group> findWithADynMemberships(final int page, final int itemsPerPage) {
+        TypedQuery<Group> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAGroup.class.getSimpleName() + " e WHERE e.aDynMemberships IS NOT EMPTY",
+                Group.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
+
+        return query.getResultList();
+    }
+
     @Transactional
     @Override
     public void refreshDynMemberships(final AnyObject anyObject) {
-        for (Group group : findAll()) {
-            for (ADynGroupMembership memb : group.getADynMemberships()) {
-                if (searchDAO().matches(
-                        anyObject,
-                        buildDynMembershipCond(memb.getFIQLCond(), group.getRealm()))) {
-
-                    memb.add(anyObject);
-                } else {
-                    memb.getMembers().remove(anyObject);
+        Query countQuery = entityManager().createQuery(
+                "SELECT COUNT(e) FROM  " + JPAGroup.class.getSimpleName() + " e WHERE e.aDynMemberships IS NOT EMPTY");
+        int count = ((Number) countQuery.getSingleResult()).intValue();
+
+        for (int page = 1; page <= (count / DEFAULT_PAGE_SIZE) + 1; page++) {
+            for (Group group : findWithADynMemberships(page, DEFAULT_PAGE_SIZE)) {
+                if (!group.getADynMemberships().isEmpty()) {
+                    for (ADynGroupMembership memb : group.getADynMemberships()) {
+                        if (jpaAnySearchDAO().matches(
+                                anyObject,
+                                buildDynMembershipCond(memb.getFIQLCond(), group.getRealm()))) {
+
+                            memb.add(anyObject);
+                            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+                        } else {
+                            memb.getMembers().remove(anyObject);
+                            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+                        }
+                    }
                 }
             }
         }
+
+    }
+
+    private List<Group> findWithUDynMemberships(final int page, final int itemsPerPage) {
+        TypedQuery<Group> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAGroup.class.getSimpleName() + " e WHERE e.uDynMembership IS NOT NULL",
+                Group.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
+
+        return query.getResultList();
     }
 
     @Transactional
     @Override
     public void refreshDynMemberships(final User user) {
-        for (Group group : findAll()) {
-            if (group.getUDynMembership() != null) {
-                if (searchDAO().matches(
-                        user,
-                        buildDynMembershipCond(group.getUDynMembership().getFIQLCond(), group.getRealm()))) {
-
-                    group.getUDynMembership().add(user);
-                } else {
-                    group.getUDynMembership().getMembers().remove(user);
+        Query countQuery = entityManager().createQuery(
+                "SELECT COUNT(e) FROM  " + JPAGroup.class.getSimpleName() + " e WHERE e.uDynMembership IS NOT NULL");
+        int count = ((Number) countQuery.getSingleResult()).intValue();
+
+        for (int page = 1; page <= (count / DEFAULT_PAGE_SIZE) + 1; page++) {
+            for (Group group : findWithUDynMemberships(page, DEFAULT_PAGE_SIZE)) {
+                if (group.getUDynMembership() != null) {
+                    if (jpaAnySearchDAO().matches(
+                            user,
+                            buildDynMembershipCond(group.getUDynMembership().getFIQLCond(), group.getRealm()))) {
+
+                        group.getUDynMembership().add(user);
+                        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+                    } else {
+                        group.getUDynMembership().getMembers().remove(user);
+                        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, group));
+                    }
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
index dbf4f66..a5cabbb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
@@ -31,12 +31,18 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.JPARole;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
 @Repository
 public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
 
+    @Autowired
+    private ApplicationEventPublisher publisher;
+
     private AnySearchDAO searchDAO;
 
     private AnySearchDAO searchDAO() {
@@ -85,6 +91,7 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
             role.getDynMembership().getMembers().clear();
             for (User user : matchingUsers) {
                 role.getDynMembership().add(user);
+                publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
             }
         }
 
@@ -99,6 +106,7 @@ public class JPARoleDAO extends AbstractDAO<Role> implements RoleDAO {
 
         for (User user : query.getResultList()) {
             user.getRoles().remove(role);
+            publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, user));
         }
 
         entityManager().remove(role);

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 3dff783..a6b4383 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -76,6 +76,8 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.event.AnyDeletedEvent;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
 import org.springframework.stereotype.Repository;
@@ -270,6 +272,16 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
         return policies;
     }
 
+    @Override
+    public List<User> findAll(final int page, final int itemsPerPage) {
+        TypedQuery<User> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAUser.class.getSimpleName() + " e", User.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
+
+        return query.getResultList();
+    }
+
     private List<AccountPolicy> getAccountPolicies(final User user) {
         List<AccountPolicy> policies = new ArrayList<>();
 
@@ -430,6 +442,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
             throw e;
         }
 
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
+
         roleDAO.refreshDynMemberships(merged);
         groupDAO().refreshDynMemberships(merged);
 
@@ -451,6 +465,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
         }
 
         entityManager().remove(user);
+        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.USER, user.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
@@ -561,7 +576,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public Collection<String> findAllResourceNames(final String key) {
+    public Collection<String> findAllResourceKeys(final String key) {
         return CollectionUtils.collect(findAllResources(authFind(key)), EntityUtils.keyTransformer());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/resources/persistence.properties
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/persistence.properties b/core/persistence-jpa/src/main/resources/persistence.properties
index 5aa696c..7f2f6f6 100644
--- a/core/persistence-jpa/src/main/resources/persistence.properties
+++ b/core/persistence-jpa/src/main/resources/persistence.properties
@@ -15,3 +15,4 @@
 # specific language governing permissions and limitations
 # under the License.
 content.directory=${conf.directory}
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/resources/persistenceContext.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/persistenceContext.xml b/core/persistence-jpa/src/main/resources/persistenceContext.xml
index 4d12fa1..50153a4 100644
--- a/core/persistence-jpa/src/main/resources/persistenceContext.xml
+++ b/core/persistence-jpa/src/main/resources/persistenceContext.xml
@@ -30,6 +30,7 @@ under the License.
   <context:annotation-config/>
   
   <context:component-scan base-package="org.apache.syncope.core.persistence.jpa"/>
+  <bean class="${any.search.dao}"/>
 
   <bean class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java
index cc324c6..3f4d48a 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyObjectTest.java
@@ -47,7 +47,7 @@ public class AnyObjectTest extends AbstractTest {
 
     @Test
     public void findAll() {
-        List<AnyObject> list = anyObjectDAO.findAll();
+        List<AnyObject> list = anyObjectDAO.findAll(1, 100);
         assertFalse(list.isEmpty());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
index 2d7e0ce..c3ef956 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/GroupTest.java
@@ -47,7 +47,7 @@ public class GroupTest extends AbstractTest {
 
     @Test
     public void findAll() {
-        List<Group> list = groupDAO.findAll();
+        List<Group> list = groupDAO.findAll(1, 100);
         assertEquals("did not get expected number of groups ", 16, list.size());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
index 78dd527..80077a7 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/MultitenancyTest.java
@@ -95,7 +95,7 @@ public class MultitenancyTest extends AbstractTest {
     @Test
     public void createUser() {
         assertNull(realmDAO.getRoot().getPasswordPolicy());
-        assertTrue(userDAO.findAll().isEmpty());
+        assertTrue(userDAO.findAll(1, 100).isEmpty());
 
         User user = entityFactory.newEntity(User.class);
         user.setRealm(realmDAO.getRoot());

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
index 730a6c2..9988f5a 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
@@ -26,7 +26,6 @@ import static org.junit.Assert.fail;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
-import java.util.UUID;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
@@ -56,7 +55,7 @@ public class UserTest extends AbstractTest {
 
     @Test
     public void findAll() {
-        List<User> list = userDAO.findAll();
+        List<User> list = userDAO.findAll(1, 100);
         assertEquals("did not get expected number of users", 5, list.size());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
index 17c45ba..f91aa7e 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java
@@ -22,9 +22,7 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.StandardEntitlement;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -60,8 +58,7 @@ public class AnySearchTest extends AbstractTest {
 
     @Test
     public void issueSYNCOPE95() {
-        Set<Group> groups = new HashSet<>(groupDAO.findAll());
-        for (Group group : groups) {
+        for (Group group : groupDAO.findAll(1, 100)) {
             groupDAO.delete(group.getKey());
         }
         groupDAO.flush();

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
index 0f915ea..ede95f7 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/UserTest.java
@@ -82,9 +82,6 @@ public class UserTest extends AbstractTest {
     public void delete() {
         List<UMembership> memberships = groupDAO.findUMemberships(groupDAO.findByName("managingDirector"));
         assertFalse(memberships.isEmpty());
-        List<URelationship> relationships = anyObjectDAO.findURelationships(
-                anyObjectDAO.find("fc6dbc3a-6c07-4965-8781-921e7401a4a5"));
-        assertFalse(relationships.isEmpty());
 
         userDAO.delete("c9b2dec2-00a7-4855-97c0-d854842b4b24");
 
@@ -97,9 +94,6 @@ public class UserTest extends AbstractTest {
 
         memberships = groupDAO.findUMemberships(groupDAO.findByName("managingDirector"));
         assertTrue(memberships.isEmpty());
-        relationships = anyObjectDAO.findURelationships(
-                anyObjectDAO.find("fc6dbc3a-6c07-4965-8781-921e7401a4a5"));
-        assertTrue(relationships.isEmpty());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
index 780cd49..ad966de 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAnyObjectProvisioningManager.java
@@ -176,7 +176,7 @@ public class DefaultAnyObjectProvisioningManager implements AnyObjectProvisionin
                 AnyTypeKind.ANY_OBJECT,
                 key,
                 propByRes,
-                CollectionUtils.removeAll(anyObjectDAO.findAllResourceNames(key), resources));
+                CollectionUtils.removeAll(anyObjectDAO.findAllResourceKeys(key), resources));
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
         return propagationReporter.getStatuses();

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index ae1090f..df9881da 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@ -36,13 +36,13 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.StatusPatchType;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
@@ -192,7 +192,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
             final String key, final Set<String> excludedResources, final boolean nullPriorityAsync) {
 
         PropagationByResource propByRes = new PropagationByResource();
-        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(key));
+        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceKeys(key));
 
         // Note here that we can only notify about "delete", not any other
         // task defined in workflow process definition: this because this
@@ -344,7 +344,7 @@ public class DefaultUserProvisioningManager implements UserProvisioningManager {
                 AnyTypeKind.USER,
                 key,
                 propByRes,
-                CollectionUtils.removeAll(userDAO.findAllResourceNames(key), resources));
+                CollectionUtils.removeAll(userDAO.findAllResourceKeys(key), resources));
         PropagationReporter propagationReporter = taskExecutor.execute(tasks, nullPriorityAsync);
 
         return propagationReporter.getStatuses();

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
index 1e4ac03..0a8566c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java
@@ -34,9 +34,7 @@ public class ExpiredAccessTokenCleanup extends AbstractSchedTaskJobDelegate {
             LOG.debug("Successfully deleted {} expired access tokens", deleted);
         }
 
-        return (dryRun
-                ? "DRY "
-                : "") + "RUNNING";
+        return "SUCCESS";
     }
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
index 36e59a2..8b3f4e5 100755
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/IdentityRecertification.java
@@ -18,13 +18,10 @@
  */
 package org.apache.syncope.core.provisioning.java.job;
 
-import java.util.Collections;
-import java.util.Date;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 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.conf.CPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.persistence.api.entity.user.User;
@@ -36,8 +33,6 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
 
     private static final String RECERTIFICATION_TIME = "identity.recertification.day.interval";
 
-    private static final int PAGE_SIZE = 10;
-
     @Autowired
     private ConfDAO confDAO;
 
@@ -65,27 +60,22 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
         }
     }
 
-    protected boolean isToBeRecertified(final User user) {
-        Date lastCertificationDate = user.getLastRecertification();
+    protected boolean isToBeRecertified(final User user, final long now) {
+        if (!user.isSuspended()
+                && (user.getLastRecertification() == null
+                || user.getLastRecertification().getTime() + recertificationTime < now)) {
 
-        if (lastCertificationDate != null) {
-            if (lastCertificationDate.getTime() + recertificationTime < System.currentTimeMillis()) {
-                LOG.debug("{} is to be recertified", user);
-                return true;
-            } else {
-                LOG.debug("{} do not need to be recertified", user);
-                return false;
-            }
+            LOG.debug("{} is to be recertified", user);
+            return true;
         }
 
-        return true;
+        LOG.debug("{} does not need to be recertified", user);
+        return false;
     }
 
     @Override
     protected String doExecute(final boolean dryRun) throws JobExecutionException {
-        LOG.info("IdentityRecertification {} running [SchedTask {}]", (dryRun
-                ? "dry "
-                : ""), task.getKey());
+        LOG.info("IdentityRecertification {} running [SchedTask {}]", dryRun ? "dry " : "", task.getKey());
 
         init();
         if (recertificationTime == -1) {
@@ -93,13 +83,16 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
             return ("IDENTITY RECERTIFICATION DISABLED");
         }
 
-        for (int page = 1; page <= (userDAO.count() / PAGE_SIZE) + 1; page++) {
-            for (User user : userDAO.findAll(
-                    SyncopeConstants.FULL_ADMIN_REALMS, page, PAGE_SIZE, Collections.<OrderByClause>emptyList())) {
+        if (dryRun) {
+            return "DRY RUN";
+        }
 
+        long now = System.currentTimeMillis();
+        for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            for (User user : userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
                 LOG.debug("Processing user: {}", user.getUsername());
 
-                if (StringUtils.isNotBlank(user.getWorkflowId()) && isToBeRecertified(user) && !dryRun) {
+                if (StringUtils.isNotBlank(user.getWorkflowId()) && isToBeRecertified(user, now)) {
                     uwfAdapter.requestCertify(user);
                 } else {
                     LOG.warn("Workflow for {} is null or empty", user);
@@ -107,9 +100,7 @@ public class IdentityRecertification extends AbstractSchedTaskJobDelegate {
             }
         }
 
-        return (dryRun
-                ? "DRY "
-                : "") + "RUNNING";
+        return "SUCCESS";
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 54c53c2..5464276 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -312,7 +312,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                 switch (task.getAnyTypeKind()) {
                     case USER:
                         try {
-                            resources = userDAO.findAllResourceNames(task.getEntityKey());
+                            resources = userDAO.findAllResourceKeys(task.getEntityKey());
                         } catch (Exception e) {
                             LOG.error("Could not read user {}", task.getEntityKey(), e);
                         }
@@ -329,7 +329,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
                     case ANY_OBJECT:
                     default:
                         try {
-                            resources = anyObjectDAO.findAllResourceNames(task.getEntityKey());
+                            resources = anyObjectDAO.findAllResourceKeys(task.getEntityKey());
                         } catch (Exception e) {
                             LOG.error("Could not read any object {}", task.getEntityKey(), e);
                         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
index b3035a1..f1ec799 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
@@ -229,7 +229,7 @@ public class PropagationManagerImpl implements PropagationManager {
             origPropByRes.merge(wfResult.getPropByRes());
 
             Set<String> pwdResourceNames = new HashSet<>(userPatch.getPassword().getResources());
-            Collection<String> currentResourceNames = userDAO.findAllResourceNames(userPatch.getKey());
+            Collection<String> currentResourceNames = userDAO.findAllResourceKeys(userPatch.getKey());
             pwdResourceNames.retainAll(currentResourceNames);
             PropagationByResource pwdPropByRes = new PropagationByResource();
             pwdPropByRes.addAll(ResourceOperation.UPDATE, pwdResourceNames);

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
index 8e18ab5..7c3bbe3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
@@ -33,6 +33,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 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.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
@@ -51,8 +52,6 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
 
 public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
 
-    private static final int PAGE_SIZE = 1000;
-
     /**
      * User DAO.
      */
@@ -201,15 +200,21 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
                         ? null
                         : pushTask.getFilter(provision.getAnyType()).getFIQLCond();
                 if (StringUtils.isBlank(filter)) {
-                    doHandle(anyDAO.findAll(), handler, pushTask.getResource());
+                    for (int page = 1; page <= (anyDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                        doHandle(anyDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE), handler, pushTask.getResource());
+                    }
                 } else {
-                    int count = anyDAO.count(SyncopeConstants.FULL_ADMIN_REALMS);
-                    for (int page = 1; page <= (count / PAGE_SIZE) + 1; page++) {
+                    SearchCond cond = SearchCondConverter.convert(filter);
+                    int count = searchDAO.count(
+                            SyncopeConstants.FULL_ADMIN_REALMS,
+                            cond,
+                            provision.getAnyType().getKind());
+                    for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
                         List<? extends Any<?>> anys = searchDAO.search(
                                 SyncopeConstants.FULL_ADMIN_REALMS,
-                                SearchCondConverter.convert(filter),
+                                cond,
                                 page,
-                                PAGE_SIZE,
+                                AnyDAO.DEFAULT_PAGE_SIZE,
                                 Collections.<OrderByClause>emptyList(),
                                 provision.getAnyType().getKind());
                         doHandle(anys, handler, pushTask.getResource());

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
new file mode 100644
index 0000000..c252744
--- /dev/null
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyCreatedUpdatedEvent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.spring.event;
+
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.springframework.context.ApplicationEvent;
+
+public class AnyCreatedUpdatedEvent<A extends Any<?>> extends ApplicationEvent {
+
+    private static final long serialVersionUID = -781747175059834365L;
+
+    private final A any;
+
+    public AnyCreatedUpdatedEvent(final Object source, final A any) {
+        super(source);
+        this.any = any;
+    }
+
+    public A getAny() {
+        return any;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
----------------------------------------------------------------------
diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
new file mode 100644
index 0000000..bd180de
--- /dev/null
+++ b/core/spring/src/main/java/org/apache/syncope/core/spring/event/AnyDeletedEvent.java
@@ -0,0 +1,44 @@
+/*
+ * 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.spring.event;
+
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.springframework.context.ApplicationEvent;
+
+public class AnyDeletedEvent extends ApplicationEvent {
+
+    private final AnyTypeKind anyTypeKind;
+
+    private final String anyKey;
+
+    public AnyDeletedEvent(final Object source, final AnyTypeKind anyTypeKind, final String anyKey) {
+        super(source);
+        this.anyTypeKind = anyTypeKind;
+        this.anyKey = anyKey;
+    }
+
+    public AnyTypeKind getAnyTypeKind() {
+        return anyTypeKind;
+    }
+
+    public String getAnyKey() {
+        return anyKey;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-activiti/pom.xml
----------------------------------------------------------------------
diff --git a/core/workflow-activiti/pom.xml b/core/workflow-activiti/pom.xml
index 11ed455..72aa4a5 100644
--- a/core/workflow-activiti/pom.xml
+++ b/core/workflow-activiti/pom.xml
@@ -74,11 +74,6 @@ under the License.
       <artifactId>syncope-core-workflow-java</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.apache.syncope.core</groupId>
-      <artifactId>syncope-core-spring</artifactId>
-      <version>${project.version}</version>
-    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
index 569dce1..82e7e01 100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
@@ -221,21 +221,12 @@ public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     }
 
     @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
+    protected WorkflowResult<Pair<String, Boolean>> doCreate(
+            final UserTO userTO,
+            final boolean disablePwdPolicyCheck,
+            final Boolean enabled,
             final boolean storePassword) {
 
-        return create(userTO, disablePwdPolicyCheck, null, storePassword);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean storePassword) {
-        return create(userTO, false, storePassword);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
-            final Boolean enabled, final boolean storePassword) {
-
         Map<String, Object> variables = new HashMap<>();
         variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
         variables.put(USER_TO, userTO);

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java
----------------------------------------------------------------------
diff --git a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java
index 46d82a3..992e3d1 100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeGroupQueryImpl.java
@@ -27,6 +27,7 @@ import org.activiti.engine.identity.GroupQuery;
 import org.activiti.engine.impl.persistence.entity.GroupEntity;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 
 public class SyncopeGroupQueryImpl implements GroupQuery {
@@ -111,15 +112,17 @@ public class SyncopeGroupQueryImpl implements GroupQuery {
             }
         }
         if (result == null) {
-            result = CollectionUtils.collect(groupDAO.findAll(),
-                    new Transformer<org.apache.syncope.core.persistence.api.entity.group.Group, Group>() {
-
-                @Override
-                public Group transform(final org.apache.syncope.core.persistence.api.entity.group.Group user) {
-                    return fromSyncopeGroup(user);
-                }
-
-            }, new ArrayList<Group>());
+            result = new ArrayList<>();
+            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                CollectionUtils.collect(groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE),
+                        new Transformer<org.apache.syncope.core.persistence.api.entity.group.Group, Group>() {
+
+                    @Override
+                    public Group transform(final org.apache.syncope.core.persistence.api.entity.group.Group group) {
+                        return fromSyncopeGroup(group);
+                    }
+                }, result);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java
----------------------------------------------------------------------
diff --git a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java
index dd10566..b7fa2e9 100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/SyncopeUserQueryImpl.java
@@ -27,6 +27,7 @@ import org.activiti.engine.identity.UserQuery;
 import org.activiti.engine.impl.persistence.entity.UserEntity;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -156,15 +157,17 @@ public class SyncopeUserQueryImpl implements UserQuery {
         }
         // THIS CAN BE *VERY* DANGEROUS
         if (result == null) {
-            result = CollectionUtils.collect(userDAO.findAll(),
-                    new Transformer<org.apache.syncope.core.persistence.api.entity.user.User, User>() {
-
-                @Override
-                public User transform(final org.apache.syncope.core.persistence.api.entity.user.User user) {
-                    return fromSyncopeUser(user);
-                }
-
-            }, new ArrayList<User>());
+            result = new ArrayList<>();
+            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                CollectionUtils.collect(userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE),
+                        new Transformer<org.apache.syncope.core.persistence.api.entity.user.User, User>() {
+
+                    @Override
+                    public User transform(final org.apache.syncope.core.persistence.api.entity.user.User user) {
+                        return fromSyncopeUser(user);
+                    }
+                }, result);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-flowable/pom.xml
----------------------------------------------------------------------
diff --git a/core/workflow-flowable/pom.xml b/core/workflow-flowable/pom.xml
index 8122d9e..cf39f92 100644
--- a/core/workflow-flowable/pom.xml
+++ b/core/workflow-flowable/pom.xml
@@ -74,11 +74,6 @@ under the License.
       <artifactId>syncope-core-workflow-java</artifactId>
       <version>${project.version}</version>
     </dependency>
-    <dependency>
-      <groupId>org.apache.syncope.core</groupId>
-      <artifactId>syncope-core-spring</artifactId>
-      <version>${project.version}</version>
-    </dependency>
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java b/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java
index 82b9057..5ecf2ce 100644
--- a/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java
+++ b/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/FlowableUserWorkflowAdapter.java
@@ -221,21 +221,12 @@ public class FlowableUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     }
 
     @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
+    protected WorkflowResult<Pair<String, Boolean>> doCreate(
+            final UserTO userTO,
+            final boolean disablePwdPolicyCheck,
+            final Boolean enabled,
             final boolean storePassword) {
 
-        return create(userTO, disablePwdPolicyCheck, null, storePassword);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean storePassword) {
-        return create(userTO, false, storePassword);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
-            final Boolean enabled, final boolean storePassword) {
-
         Map<String, Object> variables = new HashMap<>();
         variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
         variables.put(USER_TO, userTO);

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeGroupQueryImpl.java
----------------------------------------------------------------------
diff --git a/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeGroupQueryImpl.java b/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeGroupQueryImpl.java
index b3b32cd..daeb08a 100644
--- a/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeGroupQueryImpl.java
+++ b/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeGroupQueryImpl.java
@@ -27,6 +27,7 @@ import org.activiti.engine.identity.GroupQuery;
 import org.activiti.engine.impl.persistence.entity.GroupEntity;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 
 public class SyncopeGroupQueryImpl implements GroupQuery {
@@ -111,15 +112,17 @@ public class SyncopeGroupQueryImpl implements GroupQuery {
             }
         }
         if (result == null) {
-            result = CollectionUtils.collect(groupDAO.findAll(),
-                    new Transformer<org.apache.syncope.core.persistence.api.entity.group.Group, Group>() {
-
-                @Override
-                public Group transform(final org.apache.syncope.core.persistence.api.entity.group.Group user) {
-                    return fromSyncopeGroup(user);
-                }
-
-            }, new ArrayList<Group>());
+            result = new ArrayList<>();
+            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                CollectionUtils.collect(groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE),
+                        new Transformer<org.apache.syncope.core.persistence.api.entity.group.Group, Group>() {
+
+                    @Override
+                    public Group transform(final org.apache.syncope.core.persistence.api.entity.group.Group group) {
+                        return fromSyncopeGroup(group);
+                    }
+                }, result);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeUserQueryImpl.java
----------------------------------------------------------------------
diff --git a/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeUserQueryImpl.java b/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeUserQueryImpl.java
index 9231a4f..615d07d 100644
--- a/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeUserQueryImpl.java
+++ b/core/workflow-flowable/src/main/java/org/apache/syncope/core/workflow/flowable/SyncopeUserQueryImpl.java
@@ -27,6 +27,7 @@ import org.activiti.engine.identity.UserQuery;
 import org.activiti.engine.impl.persistence.entity.UserEntity;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -156,15 +157,17 @@ public class SyncopeUserQueryImpl implements UserQuery {
         }
         // THIS CAN BE *VERY* DANGEROUS
         if (result == null) {
-            result = CollectionUtils.collect(userDAO.findAll(),
-                    new Transformer<org.apache.syncope.core.persistence.api.entity.user.User, User>() {
-
-                @Override
-                public User transform(final org.apache.syncope.core.persistence.api.entity.user.User user) {
-                    return fromSyncopeUser(user);
-                }
-
-            }, new ArrayList<User>());
+            result = new ArrayList<>();
+            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                CollectionUtils.collect(userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE),
+                        new Transformer<org.apache.syncope.core.persistence.api.entity.user.User, User>() {
+
+                    @Override
+                    public User transform(final org.apache.syncope.core.persistence.api.entity.user.User user) {
+                        return fromSyncopeUser(user);
+                    }
+                }, result);
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-java/pom.xml
----------------------------------------------------------------------
diff --git a/core/workflow-java/pom.xml b/core/workflow-java/pom.xml
index 8d3fa25..4e24445 100644
--- a/core/workflow-java/pom.xml
+++ b/core/workflow-java/pom.xml
@@ -52,6 +52,11 @@ under the License.
       <artifactId>syncope-core-workflow-api</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-spring</artifactId>
+      <version>${project.version}</version>
+    </dependency>    
   </dependencies>
 
   <build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java
index df35de5..232470a 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractAnyObjectWorkflowAdapter.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.workflow.java;
 
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
@@ -48,6 +49,13 @@ public abstract class AbstractAnyObjectWorkflowAdapter
         return null;
     }
 
+    protected abstract WorkflowResult<String> doCreate(AnyObjectTO anyObjectTO);
+
+    @Override
+    public WorkflowResult<String> create(final AnyObjectTO anyObjectTO) {
+        return doCreate(anyObjectTO);
+    }
+
     protected abstract WorkflowResult<String> doUpdate(AnyObject anyObject, AnyObjectPatch anyObjectPatch);
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java
index 7b1fd3e..0689b94 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractGroupWorkflowAdapter.java
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.workflow.java;
 
 import org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -47,6 +48,13 @@ public abstract class AbstractGroupWorkflowAdapter implements GroupWorkflowAdapt
         return null;
     }
 
+    protected abstract WorkflowResult<String> doCreate(GroupTO groupTO);
+
+    @Override
+    public WorkflowResult<String> create(final GroupTO groupTO) {
+        return doCreate(groupTO);
+    }
+
     protected abstract WorkflowResult<String> doUpdate(Group group, GroupPatch groupPatch);
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
index 48c6d60..d112934 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/AbstractUserWorkflowAdapter.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.workflow.java;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
+import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.user.User;
@@ -50,12 +51,12 @@ public abstract class AbstractUserWorkflowAdapter implements UserWorkflowAdapter
     @Autowired
     protected EntityFactory entityFactory;
 
-    public static String encrypt(final String clear) {
+    protected String encrypt(final String clear) {
         byte[] encryptedBytes = EncryptorFactory.getInstance().getDefaultEncryptor().encrypt(clear.getBytes());
         return Base64.encode(encryptedBytes);
     }
 
-    public static String decrypt(final String crypted) {
+    protected String decrypt(final String crypted) {
         byte[] decryptedBytes = EncryptorFactory.getInstance().getDefaultEncryptor().decrypt(Base64.decode(crypted));
         return new String(decryptedBytes);
     }
@@ -65,6 +66,31 @@ public abstract class AbstractUserWorkflowAdapter implements UserWorkflowAdapter
         return null;
     }
 
+    @Override
+    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
+            final boolean storePassword) {
+
+        return create(userTO, disablePwdPolicyCheck, null, storePassword);
+    }
+
+    @Override
+    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean storePassword) {
+        return create(userTO, false, storePassword);
+    }
+
+    protected abstract WorkflowResult<Pair<String, Boolean>> doCreate(
+            UserTO userTO, boolean disablePwdPolicyCheck, Boolean enabled, boolean storePassword);
+
+    @Override
+    public WorkflowResult<Pair<String, Boolean>> create(
+            final UserTO userTO,
+            final boolean disablePwdPolicyCheck,
+            final Boolean enabled,
+            final boolean storePassword) {
+
+        return doCreate(userTO, disablePwdPolicyCheck, enabled, storePassword);
+    }
+
     protected abstract WorkflowResult<String> doActivate(User user, String token);
 
     @Override
@@ -148,7 +174,7 @@ public abstract class AbstractUserWorkflowAdapter implements UserWorkflowAdapter
     protected abstract void doDelete(User user);
 
     @Override
-    public void delete(final String key) {
-        doDelete(userDAO.authFind(key));
+    public void delete(final String userKey) {
+        doDelete(userDAO.authFind(userKey));
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java
index 0a866f6..dffb14f 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultAnyObjectWorkflowAdapter.java
@@ -43,7 +43,7 @@ public class DefaultAnyObjectWorkflowAdapter extends AbstractAnyObjectWorkflowAd
     }
 
     @Override
-    public WorkflowResult<String> create(final AnyObjectTO anyObjectTO) {
+    protected WorkflowResult<String> doCreate(final AnyObjectTO anyObjectTO) {
         AnyObject anyObject = entityFactory.newEntity(AnyObject.class);
         dataBinder.create(anyObject, anyObjectTO);
         anyObject = anyObjectDAO.save(anyObject);

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java
index 6763138..19949eb 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultGroupWorkflowAdapter.java
@@ -43,7 +43,7 @@ public class DefaultGroupWorkflowAdapter extends AbstractGroupWorkflowAdapter {
     }
 
     @Override
-    public WorkflowResult<String> create(final GroupTO groupTO) {
+    protected WorkflowResult<String> doCreate(final GroupTO groupTO) {
         Group group = entityFactory.newEntity(Group.class);
         dataBinder.create(group, groupTO);
         group = groupDAO.save(group);

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --git a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java
index d389e28..9a6a17c 100644
--- a/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java
+++ b/core/workflow-java/src/main/java/org/apache/syncope/core/workflow/java/DefaultUserWorkflowAdapter.java
@@ -53,21 +53,12 @@ public class DefaultUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
     }
 
     @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean storePassword) {
-        return create(userTO, false, true);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
+    protected WorkflowResult<Pair<String, Boolean>> doCreate(
+            final UserTO userTO,
+            final boolean disablePwdPolicyCheck,
+            final Boolean enabled,
             final boolean storePassword) {
 
-        return create(userTO, disablePwdPolicyCheck, null, storePassword);
-    }
-
-    @Override
-    public WorkflowResult<Pair<String, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
-            final Boolean enabled, final boolean storePassword) {
-
         User user = entityFactory.newEntity(User.class);
         dataBinder.create(user, userTO, storePassword);
 
@@ -200,7 +191,7 @@ public class DefaultUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
 
     @Override
     public WorkflowResult<String> requestCertify(final User user) {
-        throw new UnsupportedOperationException("Not supported.");
+        throw new WorkflowException(new UnsupportedOperationException("Not supported."));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
index 7ced13d..3578461 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeleteProducer.java
@@ -63,7 +63,7 @@ public class DeleteProducer extends AbstractProducer {
             switch (getAnyTypeKind()) {
                 case USER:
                     PropagationByResource propByRes = new PropagationByResource();
-                    propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(key));
+                    propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceKeys(key));
                     // Note here that we can only notify about "delete", not any other
                     // task defined in workflow process definition: this because this
                     // information could only be available after uwfAdapter.delete(), which

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
----------------------------------------------------------------------
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
index b5d5d04..5b20d11 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/producer/DeprovisionProducer.java
@@ -71,7 +71,7 @@ public class DeprovisionProducer extends AbstractProducer {
                             AnyTypeKind.USER,
                             key,
                             propByRes,
-                            CollectionUtils.removeAll(userDAO.findAllResourceNames(key), resources));
+                            CollectionUtils.removeAll(userDAO.findAllResourceKeys(key), resources));
                     propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;
@@ -93,7 +93,7 @@ public class DeprovisionProducer extends AbstractProducer {
                             AnyTypeKind.ANY_OBJECT,
                             key,
                             propByRes,
-                            CollectionUtils.removeAll(anyObjectDAO.findAllResourceNames(key), resources));
+                            CollectionUtils.removeAll(anyObjectDAO.findAllResourceKeys(key), resources));
                     propagationReporter = getPropagationTaskExecutor().execute(tasks, nullPriorityAsync);
                     exchange.getOut().setBody(propagationReporter.getStatuses());
                     break;

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/client-elasticsearch/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/pom.xml b/ext/elasticsearch/client-elasticsearch/pom.xml
new file mode 100644
index 0000000..48d7f1c
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-elasticsearch</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch Client</name>
+  <description>Apache Syncope Ext: Elasticsearch Client</description>
+  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+  <artifactId>syncope-ext-elasticsearch-client</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-spring</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+      
+    <dependency>
+      <groupId>org.elasticsearch.client</groupId>
+      <artifactId>transport</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
new file mode 100644
index 0000000..e4a30f9
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
@@ -0,0 +1,83 @@
+/*
+ * 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.ext.elasticsearch.client;
+
+import java.net.InetAddress;
+import java.util.Map;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.transport.InetSocketTransportAddress;
+import org.elasticsearch.transport.client.PreBuiltTransportClient;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+
+/**
+ * Spring {@link FactoryBean} for getting the Elasticsearch's {@link Client} singleton instance.
+ */
+public class ElasticsearchClientFactoryBean implements FactoryBean<Client>, DisposableBean {
+
+    private Map<String, Object> settings;
+
+    private Map<String, Integer> addresses;
+
+    private Client client;
+
+    public void setSettings(final Map<String, Object> settings) {
+        this.settings = settings;
+    }
+
+    public void setAddresses(final Map<String, Integer> addresses) {
+        this.addresses = addresses;
+    }
+
+    @Override
+    public Client getObject() throws Exception {
+        synchronized (this) {
+            if (client == null) {
+                PreBuiltTransportClient tClient = new PreBuiltTransportClient(Settings.builder().put(settings).build());
+
+                for (Map.Entry<String, Integer> entry : addresses.entrySet()) {
+                    tClient.addTransportAddress(
+                            new InetSocketTransportAddress(InetAddress.getByName(entry.getKey()), entry.getValue()));
+                }
+
+                client = tClient;
+            }
+        }
+        return client;
+    }
+
+    @Override
+    public Class<?> getObjectType() {
+        return Client.class;
+    }
+
+    @Override
+    public boolean isSingleton() {
+        return true;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        if (client != null) {
+            client.close();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
new file mode 100644
index 0000000..3e20acd
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
@@ -0,0 +1,95 @@
+/*
+ * 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.ext.elasticsearch.client;
+
+import java.io.IOException;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.event.AnyDeletedEvent;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.elasticsearch.action.delete.DeleteResponse;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.action.support.WriteRequest;
+import org.elasticsearch.action.update.UpdateResponse;
+import org.elasticsearch.client.Client;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.event.TransactionalEventListener;
+
+/**
+ * Listen to any create / update and delete in order to keep the Elastsicsearch indexes consistent.
+ */
+public class ElasticsearchIndexManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ElasticsearchIndexManager.class);
+
+    @Autowired
+    private Client client;
+
+    @Autowired
+    private ElasticsearchUtils elasticsearchUtils;
+
+    @TransactionalEventListener
+    public void after(final AnyCreatedUpdatedEvent<Any<?>> event) throws IOException {
+        GetResponse getResponse = client.prepareGet(AuthContextUtils.getDomain().toLowerCase(),
+                event.getAny().getType().getKind().name(),
+                event.getAny().getKey()).
+                get();
+        if (getResponse.isExists()) {
+            LOG.debug("About to update index for {}", event.getAny());
+
+            UpdateResponse response = client.prepareUpdate(
+                    AuthContextUtils.getDomain().toLowerCase(),
+                    event.getAny().getType().getKind().name(),
+                    event.getAny().getKey()).
+                    setDoc(elasticsearchUtils.builder(event.getAny())).
+                    setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).
+                    get();
+            LOG.debug("Index successfully updated for {}: {}", event.getAny(), response);
+        } else {
+            LOG.debug("About to create index for {}", event.getAny());
+
+            IndexResponse response = client.prepareIndex(
+                    AuthContextUtils.getDomain().toLowerCase(),
+                    event.getAny().getType().getKind().name(),
+                    event.getAny().getKey()).
+                    setSource(elasticsearchUtils.builder(event.getAny())).
+                    setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).
+                    get();
+
+            LOG.debug("Index successfully created for {}: {}", event.getAny(), response);
+        }
+    }
+
+    @TransactionalEventListener
+    public void after(final AnyDeletedEvent event) {
+        LOG.debug("About to delete index for {}[{}]", event.getAnyTypeKind(), event.getAnyKey());
+
+        DeleteResponse response = client.prepareDelete(
+                AuthContextUtils.getDomain().toLowerCase(),
+                event.getAnyTypeKind().name(),
+                event.getAnyKey()).
+                setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).
+                get();
+
+        LOG.debug("Index successfully deleted for {}[{}]: {}", event.getAnyTypeKind(), event.getAnyKey(), response);
+    }
+}


[4/8] syncope git commit: [SYNCOPE-1077] Extension provided, not enabled by default in fit/core-reference (profile available)

Posted by il...@apache.org.
[SYNCOPE-1077] Extension provided, not enabled by default in fit/core-reference (profile available)


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/07e31980
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/07e31980
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/07e31980

Branch: refs/heads/2_0_X
Commit: 07e31980a076f1276ee2c3667bdcb00143ec2d52
Parents: a7b31ea
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon May 8 13:32:27 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon May 8 13:32:27 2017 +0200

----------------------------------------------------------------------
 .../client/console/commons/Constants.java       |   2 +-
 .../client/enduser/resources/InfoResource.java  |   2 -
 .../enduser/resources/SchemaResource.java       |   2 -
 .../resources/UserSelfCreateResource.java       |   2 -
 .../enduser/resources/UserSelfReadResource.java |   2 -
 .../syncope/common/lib/info/PlatformInfo.java   |  10 +
 .../apache/syncope/core/logic/SyncopeLogic.java |   9 +-
 .../init/ClassPathScanImplementationLookup.java |  47 +--
 .../core/logic/report/GroupReportlet.java       |  23 +-
 .../logic/report/ReconciliationReportlet.java   |   9 +-
 .../core/logic/report/UserReportlet.java        |  23 +-
 .../core/persistence/api/dao/AnyDAO.java        |  15 +-
 .../core/persistence/api/dao/AnyObjectDAO.java  |   7 +-
 .../core/persistence/api/dao/GroupDAO.java      |   2 -
 .../core/persistence/api/dao/UserDAO.java       |   4 +-
 core/persistence-jpa/pom.xml                    |   5 -
 .../persistence/jpa/dao/AbstractAnyDAO.java     |  11 +-
 .../jpa/dao/AbstractAnySearchDAO.java           | 343 ++++++++++++++++++
 .../persistence/jpa/dao/JPAAnyObjectDAO.java    |  45 ++-
 .../persistence/jpa/dao/JPAAnySearchDAO.java    | 354 ++++--------------
 .../core/persistence/jpa/dao/JPAGroupDAO.java   | 122 +++++--
 .../core/persistence/jpa/dao/JPARoleDAO.java    |   8 +
 .../core/persistence/jpa/dao/JPAUserDAO.java    |  17 +-
 .../src/main/resources/persistence.properties   |   1 +
 .../src/main/resources/persistenceContext.xml   |   1 +
 .../persistence/jpa/inner/AnyObjectTest.java    |   2 +-
 .../core/persistence/jpa/inner/GroupTest.java   |   2 +-
 .../persistence/jpa/inner/MultitenancyTest.java |   2 +-
 .../core/persistence/jpa/inner/UserTest.java    |   3 +-
 .../persistence/jpa/outer/AnySearchTest.java    |   5 +-
 .../core/persistence/jpa/outer/UserTest.java    |   6 -
 .../DefaultAnyObjectProvisioningManager.java    |   2 +-
 .../java/DefaultUserProvisioningManager.java    |   6 +-
 .../java/job/ExpiredAccessTokenCleanup.java     |   4 +-
 .../java/job/IdentityRecertification.java       |  45 +--
 .../AbstractPropagationTaskExecutor.java        |   4 +-
 .../propagation/PropagationManagerImpl.java     |   2 +-
 .../java/pushpull/PushJobDelegate.java          |  19 +-
 .../spring/event/AnyCreatedUpdatedEvent.java    |  39 ++
 .../core/spring/event/AnyDeletedEvent.java      |  44 +++
 core/workflow-activiti/pom.xml                  |   5 -
 .../activiti/ActivitiUserWorkflowAdapter.java   |  17 +-
 .../activiti/SyncopeGroupQueryImpl.java         |  21 +-
 .../workflow/activiti/SyncopeUserQueryImpl.java |  21 +-
 core/workflow-flowable/pom.xml                  |   5 -
 .../flowable/FlowableUserWorkflowAdapter.java   |  17 +-
 .../flowable/SyncopeGroupQueryImpl.java         |  21 +-
 .../workflow/flowable/SyncopeUserQueryImpl.java |  21 +-
 core/workflow-java/pom.xml                      |   5 +
 .../java/AbstractAnyObjectWorkflowAdapter.java  |   8 +
 .../java/AbstractGroupWorkflowAdapter.java      |   8 +
 .../java/AbstractUserWorkflowAdapter.java       |  34 +-
 .../java/DefaultAnyObjectWorkflowAdapter.java   |   2 +-
 .../java/DefaultGroupWorkflowAdapter.java       |   2 +-
 .../java/DefaultUserWorkflowAdapter.java        |  19 +-
 .../camel/producer/DeleteProducer.java          |   2 +-
 .../camel/producer/DeprovisionProducer.java     |   4 +-
 ext/elasticsearch/client-elasticsearch/pom.xml  |  61 ++++
 .../client/ElasticsearchClientFactoryBean.java  |  83 +++++
 .../client/ElasticsearchIndexManager.java       |  95 +++++
 .../client/ElasticsearchUtils.java              | 194 ++++++++++
 .../resources/elasticsearchClientContext.xml    |  50 +++
 ext/elasticsearch/persistence-jpa/pom.xml       |  62 ++++
 .../jpa/dao/ElasticsearchAnySearchDAO.java      | 355 +++++++++++++++++++
 .../src/main/resources/persistence.properties   |  18 +
 ext/elasticsearch/pom.xml                       |  46 +++
 ext/elasticsearch/provisioning-java/pom.xml     |  62 ++++
 .../java/job/ElasticsearchReindex.java          | 137 +++++++
 ext/pom.xml                                     |   1 +
 fit/core-reference/pom.xml                      |  72 ++++
 .../core/reference/ITImplementationLookup.java  |  40 ++-
 .../elasticsearch/persistence.properties        |  18 +
 .../src/main/resources/elasticsearch/web.xml    |  50 +++
 .../src/main/resources/log4j2.xml               |  10 +-
 fit/core-reference/src/test/resources/rebel.xml |   9 +
 pom.xml                                         |  18 +-
 76 files changed, 2293 insertions(+), 551 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
index 11bdc72..1eedd67 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/Constants.java
@@ -45,7 +45,7 @@ public final class Constants {
 
     public static final String USERNAME_FIELD_NAME = "username";
 
-    public static final String OBJNAME_FIELD_NAME = "name";
+    public static final String NAME_FIELD_NAME = "name";
 
     public static final String DEFAULT_TOKEN_FIELD_NAME = "token";
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
index c96fa2a..23af8fc 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/InfoResource.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.enduser.resources;
 
-import static org.apache.syncope.client.enduser.resources.BaseResource.MAPPER;
-
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
index 5edd4b0..b088201 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/SchemaResource.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.enduser.resources;
 
-import static org.apache.syncope.client.enduser.resources.BaseResource.MAPPER;
-
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
index b7775b1..8b42285 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfCreateResource.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.enduser.resources;
 
-import static org.apache.syncope.client.enduser.resources.BaseResource.LOG;
-
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
----------------------------------------------------------------------
diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
index 81ffcd1..c4cb6c1 100644
--- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
+++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/resources/UserSelfReadResource.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.enduser.resources;
 
-import static org.apache.syncope.client.enduser.resources.BaseResource.MAPPER;
-
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Map;

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java b/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java
index 1f3cbd1..9a7ac61 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/info/PlatformInfo.java
@@ -65,6 +65,8 @@ public class PlatformInfo extends AbstractBaseBean {
 
     private String passwordGenerator;
 
+    private String anySearchDAO;
+
     private final Set<String> entitlements = new HashSet<>();
 
     private final Set<String> reportletConfs = new HashSet<>();
@@ -166,6 +168,14 @@ public class PlatformInfo extends AbstractBaseBean {
         this.passwordGenerator = passwordGenerator;
     }
 
+    public String getAnySearchDAO() {
+        return anySearchDAO;
+    }
+
+    public void setAnySearchDAO(final String anySearchDAO) {
+        this.anySearchDAO = anySearchDAO;
+    }
+
     @XmlElementWrapper(name = "entitlements")
     @XmlElement(name = "entitlement")
     @JsonProperty("entitlements")

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index 36cc50c..bce3c09 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -38,6 +38,7 @@ import org.apache.syncope.core.spring.security.PasswordGenerator;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.ImplementationLookup.Type;
 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.dao.ConfDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
@@ -139,6 +140,9 @@ public class SyncopeLogic extends AbstractLogic<AbstractBaseBean> {
     private PasswordGenerator passwordGenerator;
 
     @Autowired
+    private AnySearchDAO anySearchDAO;
+
+    @Autowired
     private ImplementationLookup implLookup;
 
     public boolean isSelfRegAllowed() {
@@ -176,8 +180,9 @@ public class SyncopeLogic extends AbstractLogic<AbstractBaseBean> {
                 PLATFORM_INFO.setAnyObjectProvisioningManager(AopUtils.getTargetClass(aProvisioningManager).getName());
                 PLATFORM_INFO.setUserProvisioningManager(AopUtils.getTargetClass(uProvisioningManager).getName());
                 PLATFORM_INFO.setGroupProvisioningManager(AopUtils.getTargetClass(gProvisioningManager).getName());
-                PLATFORM_INFO.setVirAttrCache(virAttrCache.getClass().getName());
-                PLATFORM_INFO.setPasswordGenerator(passwordGenerator.getClass().getName());
+                PLATFORM_INFO.setVirAttrCache(AopUtils.getTargetClass(virAttrCache).getName());
+                PLATFORM_INFO.setPasswordGenerator(AopUtils.getTargetClass(passwordGenerator).getName());
+                PLATFORM_INFO.setAnySearchDAO(AopUtils.getTargetClass(anySearchDAO).getName());
 
                 PLATFORM_INFO.getReportletConfs().addAll(implLookup.getClassNames(Type.REPORTLET_CONF));
                 PLATFORM_INFO.getAccountRules().addAll(implLookup.getClassNames(Type.ACCOUNT_RULE_CONF));

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index b91f2aa..3799150 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -28,34 +28,35 @@ import java.util.Set;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.ImplementationLookup.Type;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
 import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import org.apache.syncope.core.provisioning.api.LogicActions;
 import org.apache.syncope.core.provisioning.api.data.MappingItemTransformer;
 import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
 import org.apache.syncope.core.provisioning.api.notification.NotificationRecipientsProvider;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
+import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
+import org.apache.syncope.core.provisioning.api.pushpull.PullCorrelationRule;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
-import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
+import org.apache.syncope.core.provisioning.api.pushpull.ReconciliationFilterBuilder;
+import org.apache.syncope.core.provisioning.java.data.JEXLMappingItemTransformerImpl;
+import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate;
+import org.apache.syncope.core.provisioning.java.pushpull.PlainAttrsPullCorrelationRule;
 import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
+import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
 import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.util.ClassUtils;
-import org.apache.syncope.core.provisioning.api.pushpull.ReconciliationFilterBuilder;
-import org.apache.syncope.core.provisioning.api.pushpull.PullCorrelationRule;
-import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
-import org.apache.syncope.core.provisioning.java.data.JEXLMappingItemTransformerImpl;
-import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate;
-import org.apache.syncope.core.provisioning.java.pushpull.PlainAttrsPullCorrelationRule;
 
 /**
  * Cache class names for all implementations of Syncope interfaces found in classpath, for later usage.
@@ -119,9 +120,9 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
             try {
                 Class<?> clazz = ClassUtils.resolveClassName(
                         bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-                boolean isAbsractClazz = Modifier.isAbstract(clazz.getModifiers());
+                boolean isAbstractClazz = Modifier.isAbstract(clazz.getModifiers());
 
-                if (Reportlet.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (Reportlet.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     ReportletConfClass annotation = clazz.getAnnotation(ReportletConfClass.class);
                     if (annotation == null) {
                         LOG.warn("Found Reportlet {} without declared configuration", clazz.getName());
@@ -131,7 +132,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     }
                 }
 
-                if (AccountRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (AccountRule.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     AccountRuleConfClass annotation = clazz.getAnnotation(AccountRuleConfClass.class);
                     if (annotation == null) {
                         LOG.warn("Found account policy rule {} without declared configuration", clazz.getName());
@@ -141,7 +142,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     }
                 }
 
-                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     PasswordRuleConfClass annotation = clazz.getAnnotation(PasswordRuleConfClass.class);
                     if (annotation == null) {
                         LOG.warn("Found password policy rule {} without declared configuration", clazz.getName());
@@ -151,13 +152,13 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     }
                 }
 
-                if (MappingItemTransformer.class.isAssignableFrom(clazz) && !isAbsractClazz
+                if (MappingItemTransformer.class.isAssignableFrom(clazz) && !isAbstractClazz
                         && !clazz.equals(JEXLMappingItemTransformerImpl.class)) {
 
                     classNames.get(Type.MAPPING_ITEM_TRANSFORMER).add(clazz.getName());
                 }
 
-                if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbsractClazz
+                if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbstractClazz
                         && !PullJobDelegate.class.isAssignableFrom(clazz)
                         && !PushJobDelegate.class.isAssignableFrom(clazz)
                         && !GroupMemberProvisionTaskJobDelegate.class.isAssignableFrom(clazz)) {
@@ -165,36 +166,36 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     classNames.get(Type.TASKJOBDELEGATE).add(bd.getBeanClassName());
                 }
 
-                if (ReconciliationFilterBuilder.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (ReconciliationFilterBuilder.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.RECONCILIATION_FILTER_BUILDER).add(bd.getBeanClassName());
                 }
 
-                if (LogicActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (LogicActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.LOGIC_ACTIONS).add(bd.getBeanClassName());
                 }
 
-                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.PROPAGATION_ACTIONS).add(bd.getBeanClassName());
                 }
 
-                if (PullActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (PullActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.PULL_ACTIONS).add(bd.getBeanClassName());
                 }
 
-                if (PushActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (PushActions.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.PUSH_ACTIONS).add(bd.getBeanClassName());
                 }
 
-                if (PullCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz
+                if (PullCorrelationRule.class.isAssignableFrom(clazz) && !isAbstractClazz
                         && !PlainAttrsPullCorrelationRule.class.isAssignableFrom(clazz)) {
                     classNames.get(Type.PULL_CORRELATION_RULE).add(bd.getBeanClassName());
                 }
 
-                if (Validator.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (Validator.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.VALIDATOR).add(bd.getBeanClassName());
                 }
 
-                if (NotificationRecipientsProvider.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                if (NotificationRecipientsProvider.class.isAssignableFrom(clazz) && !isAbstractClazz) {
                     classNames.get(Type.NOTIFICATION_RECIPIENTS_PROVIDER).add(bd.getBeanClassName());
                 }
             } catch (Throwable t) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
index 4bc4a37..0aeaf0a 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
@@ -31,6 +31,7 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -47,8 +48,6 @@ import org.xml.sax.helpers.AttributesImpl;
 @ReportletConfClass(GroupReportletConf.class)
 public class GroupReportlet extends AbstractReportlet {
 
-    private static final int PAGE_SIZE = 10;
-
     @Autowired
     private GroupDAO groupDAO;
 
@@ -297,15 +296,21 @@ public class GroupReportlet extends AbstractReportlet {
 
         doExtractConf(handler);
 
-        if (StringUtils.isBlank(this.conf.getMatchingCond())) {
-            doExtract(handler, groupDAO.findAll());
-        } else {
-            for (int page = 1; page <= (count() / PAGE_SIZE) + 1; page++) {
-                List<Group> groups = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
+        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            List<Group> groups;
+            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
+                groups = groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
+            } else {
+                groups = searchDAO.search(
+                        SyncopeConstants.FULL_ADMIN_REALMS,
                         SearchCondConverter.convert(this.conf.getMatchingCond()),
-                        page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), AnyTypeKind.GROUP);
-                doExtract(handler, groups);
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        AnyTypeKind.USER);
             }
+
+            doExtract(handler, groups);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
index ac5c1f1..4922015 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReconciliationReportlet.java
@@ -35,6 +35,7 @@ import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
 import org.apache.syncope.common.lib.report.ReconciliationReportletConf.Feature;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
 import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
@@ -402,7 +403,9 @@ public class ReconciliationReportlet extends AbstractReportlet {
             atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(userDAO.count()));
             handler.startElement("", "", getAnyElementName(AnyTypeKind.USER) + "s", atts);
 
-            doExtract(handler, userDAO.findAll());
+            for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                doExtract(handler, userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
+            }
         } else {
             SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
 
@@ -419,7 +422,9 @@ public class ReconciliationReportlet extends AbstractReportlet {
             atts.addAttribute("", "", "total", ReportXMLConst.XSD_INT, String.valueOf(groupDAO.count()));
             handler.startElement("", "", getAnyElementName(AnyTypeKind.GROUP) + "s", atts);
 
-            doExtract(handler, groupDAO.findAll());
+            for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                doExtract(handler, groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE));
+            }
         } else {
             SearchCond cond = SearchCondConverter.convert(this.conf.getUserMatchingCond());
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
index 38fba8a..bb1048b 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
@@ -33,6 +33,7 @@ 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.core.persistence.api.dao.AnyDAO;
 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.user.User;
@@ -53,8 +54,6 @@ import org.xml.sax.helpers.AttributesImpl;
 @ReportletConfClass(UserReportletConf.class)
 public class UserReportlet extends AbstractReportlet {
 
-    private static final int PAGE_SIZE = 10;
-
     @Autowired
     private UserDAO userDAO;
 
@@ -364,15 +363,21 @@ public class UserReportlet extends AbstractReportlet {
 
         doExtractConf(handler);
 
-        if (StringUtils.isBlank(this.conf.getMatchingCond())) {
-            doExtract(handler, userDAO.findAll());
-        } else {
-            for (int page = 1; page <= (count() / PAGE_SIZE) + 1; page++) {
-                List<User> users = searchDAO.search(SyncopeConstants.FULL_ADMIN_REALMS,
+        for (int page = 1; page <= (count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+            List<User> users;
+            if (StringUtils.isBlank(this.conf.getMatchingCond())) {
+                users = userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE);
+            } else {
+                users = searchDAO.search(
+                        SyncopeConstants.FULL_ADMIN_REALMS,
                         SearchCondConverter.convert(this.conf.getMatchingCond()),
-                        page, PAGE_SIZE, Collections.<OrderByClause>emptyList(), AnyTypeKind.USER);
-                doExtract(handler, users);
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        AnyTypeKind.USER);
             }
+
+            doExtract(handler, users);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
index 6b2fd4e..44c0c8c 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java
@@ -28,6 +28,8 @@ import org.apache.syncope.core.persistence.api.entity.Schema;
 
 public interface AnyDAO<A extends Any<?>> extends DAO<A> {
 
+    int DEFAULT_PAGE_SIZE = 10;
+
     A authFind(String key);
 
     A find(String key);
@@ -53,11 +55,18 @@ public interface AnyDAO<A extends Any<?>> extends DAO<A> {
     List<A> findByResource(ExternalResource resource);
 
     /**
-     * Find any objects without any limitation.
+     * @return the total number of any objects of type {@link A}
+     */
+    int count();
+
+    /**
+     * Find any objects without any limitation, according to given page and items per page.
      *
-     * @return all any objects of type {@link A} available.
+     * @param page search result page
+     * @param itemsPerPage items per search result page
+     * @return any objects of type {@link A} matching the provided conditions
      */
-    List<A> findAll();
+    List<A> findAll(int page, int itemsPerPage);
 
     /**
      * Find any objects visible from the given admin realms, according to given page and items per page, sorted as

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
index d399ac8..c3a4cd7 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyObjectDAO.java
@@ -26,7 +26,6 @@ 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.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.user.URelationship;
 
 public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
@@ -46,9 +45,7 @@ public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
     List<Group> findDynGroupMemberships(AnyObject anyObject);
 
-    List<ARelationship> findARelationships(AnyObject anyObject);
-
-    List<URelationship> findURelationships(AnyObject anyObject);
+    List<ARelationship> findAllARelationships(AnyObject anyObject);
 
     Collection<Group> findAllGroups(AnyObject anyObject);
 
@@ -56,5 +53,5 @@ public interface AnyObjectDAO extends AnyDAO<AnyObject> {
 
     Collection<ExternalResource> findAllResources(AnyObject anyObject);
 
-    Collection<String> findAllResourceNames(String key);
+    Collection<String> findAllResourceKeys(String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java
index aa30108..c74e430 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/GroupDAO.java
@@ -30,8 +30,6 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 
 public interface GroupDAO extends AnyDAO<Group> {
 
-    int count();
-
     Map<String, Integer> countByRealm();
 
     Group findByName(String name);

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
index d577327..55efa93 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/UserDAO.java
@@ -30,8 +30,6 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 
 public interface UserDAO extends AnyDAO<User> {
 
-    int count();
-
     Map<String, Integer> countByRealm();
 
     Map<String, Integer> countByStatus();
@@ -58,7 +56,7 @@ public interface UserDAO extends AnyDAO<User> {
 
     Collection<ExternalResource> findAllResources(User user);
 
-    Collection<String> findAllResourceNames(String key);
+    Collection<String> findAllResourceKeys(String key);
 
     Pair<Boolean, Boolean> enforcePolicies(User user);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/pom.xml b/core/persistence-jpa/pom.xml
index b97cca3..87d0efe 100644
--- a/core/persistence-jpa/pom.xml
+++ b/core/persistence-jpa/pom.xml
@@ -98,11 +98,6 @@ under the License.
     </dependency>
       
     <dependency>
-      <groupId>org.quartz-scheduler</groupId>
-      <artifactId>quartz</artifactId>
-    </dependency>
-      
-    <dependency>
       <groupId>org.apache.syncope.core</groupId>
       <artifactId>syncope-core-workflow-api</artifactId>
       <version>${project.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
index 25bfe50..f348a06 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
@@ -36,7 +36,6 @@ import org.apache.commons.jexl3.parser.Parser;
 import org.apache.commons.jexl3.parser.ParserConstants;
 import org.apache.commons.jexl3.parser.Token;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.persistence.api.dao.AllowedSchemas;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
@@ -64,11 +63,16 @@ import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractPlainAttrValue;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> implements AnyDAO<A> {
 
+    @Autowired
+    protected ApplicationEventPublisher publisher;
+
     private PlainSchemaDAO plainSchemaDAO;
 
     private DerSchemaDAO derSchemaDAO;
@@ -425,11 +429,6 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
         return query.getResultList();
     }
 
-    @Override
-    public final List<A> findAll() {
-        return findAll(SyncopeConstants.FULL_ADMIN_REALMS, -1, -1, Collections.<OrderByClause>emptyList());
-    }
-
     private SearchCond getAllMatchingCond() {
         AnyCond idCond = new AnyCond(AttributeCond.Type.ISNOTNULL);
         idCond.setSchema("id");

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
new file mode 100644
index 0000000..d9b4e95
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnySearchDAO.java
@@ -0,0 +1,343 @@
+/*
+ * 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.persistence.jpa.dao;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.persistence.Entity;
+import javax.validation.ValidationException;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+import org.apache.commons.lang3.ClassUtils;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+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.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
+import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+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.AnyUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+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.persistence.jpa.entity.JPAPlainSchema;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ReflectionUtils;
+
+public abstract class AbstractAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO {
+
+    @Autowired
+    protected RealmDAO realmDAO;
+
+    @Autowired
+    protected AnyObjectDAO anyObjectDAO;
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected GroupDAO groupDAO;
+
+    @Autowired
+    protected PlainSchemaDAO schemaDAO;
+
+    @Autowired
+    protected AnyUtilsFactory anyUtilsFactory;
+
+    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
+    @Override
+    public <T extends Any<?>> List<T> searchAssignable(final String realmFullPath, final AnyTypeKind kind) {
+        AssignableCond assignableCond = new AssignableCond();
+        assignableCond.setRealmFullPath(realmFullPath);
+        return search(SearchCond.getLeafCond(assignableCond), kind);
+    }
+
+    protected abstract int doCount(Set<String> adminRealms, SearchCond cond, AnyTypeKind kind);
+
+    @Override
+    public int count(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
+        if (adminRealms == null || adminRealms.isEmpty()) {
+            LOG.error("No realms provided");
+            return 0;
+        }
+
+        LOG.debug("Search condition:\n{}", cond);
+        if (cond == null || !cond.isValid()) {
+            LOG.error("Invalid search condition:\n{}", cond);
+            return 0;
+        }
+
+        return doCount(adminRealms, cond, kind);
+    }
+
+    @Override
+    public <T extends Any<?>> List<T> search(final SearchCond cond, final AnyTypeKind kind) {
+        return search(cond, Collections.<OrderByClause>emptyList(), kind);
+    }
+
+    @Override
+    public <T extends Any<?>> List<T> search(
+            final SearchCond cond, final List<OrderByClause> orderBy, final AnyTypeKind kind) {
+
+        return search(SyncopeConstants.FULL_ADMIN_REALMS, cond, -1, -1, orderBy, kind);
+    }
+
+    protected abstract <T extends Any<?>> List<T> doSearch(
+            Set<String> adminRealms,
+            SearchCond searchCondition,
+            int page,
+            int itemsPerPage,
+            List<OrderByClause> orderBy,
+            AnyTypeKind kind);
+
+    protected Pair<PlainSchema, PlainAttrValue> check(final AttributeCond cond, final AnyTypeKind kind) {
+        AnyUtils attrUtils = anyUtilsFactory.getInstance(kind);
+
+        PlainSchema schema = schemaDAO.find(cond.getSchema());
+        if (schema == null) {
+            LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
+            throw new IllegalArgumentException();
+        }
+
+        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        try {
+            if (cond.getType() != AttributeCond.Type.LIKE
+                    && cond.getType() != AttributeCond.Type.ILIKE
+                    && cond.getType() != AttributeCond.Type.ISNULL
+                    && cond.getType() != AttributeCond.Type.ISNOTNULL) {
+
+                schema.getValidator().validate(cond.getExpression(), attrValue);
+            }
+        } catch (ValidationException e) {
+            LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
+            throw new IllegalArgumentException();
+        }
+
+        return Pair.of(schema, attrValue);
+    }
+
+    protected Triple<PlainSchema, PlainAttrValue, AnyCond> check(final AnyCond cond, final AnyTypeKind kind) {
+        AnyCond condClone = SerializationUtils.clone(cond);
+
+        AnyUtils attrUtils = anyUtilsFactory.getInstance(kind);
+
+        // Keeps track of difference between entity's getKey() and JPA @Id fields
+        if ("key".equals(condClone.getSchema())) {
+            condClone.setSchema("id");
+        }
+
+        Field anyField = ReflectionUtils.findField(attrUtils.anyClass(), condClone.getSchema());
+        if (anyField == null) {
+            LOG.warn("Ignoring invalid schema '{}'", condClone.getSchema());
+            throw new IllegalArgumentException();
+        }
+
+        PlainSchema schema = new JPAPlainSchema();
+        schema.setKey(anyField.getName());
+        for (AttrSchemaType attrSchemaType : AttrSchemaType.values()) {
+            if (anyField.getType().isAssignableFrom(attrSchemaType.getType())) {
+                schema.setType(attrSchemaType);
+            }
+        }
+
+        // Deal with any Integer fields logically mapping to boolean values
+        boolean foundBooleanMin = false;
+        boolean foundBooleanMax = false;
+        if (Integer.class.equals(anyField.getType())) {
+            for (Annotation annotation : anyField.getAnnotations()) {
+                if (Min.class.equals(annotation.annotationType())) {
+                    foundBooleanMin = ((Min) annotation).value() == 0;
+                } else if (Max.class.equals(annotation.annotationType())) {
+                    foundBooleanMax = ((Max) annotation).value() == 1;
+                }
+            }
+        }
+        if (foundBooleanMin && foundBooleanMax) {
+            schema.setType(AttrSchemaType.Boolean);
+        }
+
+        // Deal with any fields representing relationships to other entities
+        if (anyField.getType().getAnnotation(Entity.class) != null) {
+            Method relMethod = null;
+            try {
+                relMethod = ClassUtils.getPublicMethod(anyField.getType(), "getKey", new Class<?>[0]);
+            } catch (Exception e) {
+                LOG.error("Could not find {}#getKey", anyField.getType(), e);
+            }
+
+            if (relMethod != null && String.class.isAssignableFrom(relMethod.getReturnType())) {
+                condClone.setSchema(condClone.getSchema() + "_id");
+                schema.setType(AttrSchemaType.String);
+            }
+        }
+
+        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        if (condClone.getType() != AttributeCond.Type.LIKE
+                && condClone.getType() != AttributeCond.Type.ILIKE
+                && condClone.getType() != AttributeCond.Type.ISNULL
+                && condClone.getType() != AttributeCond.Type.ISNOTNULL) {
+
+            try {
+                schema.getValidator().validate(condClone.getExpression(), attrValue);
+            } catch (ValidationException e) {
+                LOG.error("Could not validate expression '" + condClone.getExpression() + "'", e);
+                throw new IllegalArgumentException();
+            }
+        }
+
+        return Triple.of(schema, attrValue, condClone);
+    }
+
+    protected String check(final MembershipCond cond) {
+        String groupKey;
+        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getGroup()).matches()) {
+            groupKey = cond.getGroup();
+        } else {
+            Group group = groupDAO.findByName(cond.getGroup());
+            groupKey = group == null ? null : group.getKey();
+        }
+        if (groupKey == null) {
+            LOG.error("Could not find group for '" + cond.getGroup() + "'");
+            throw new IllegalArgumentException();
+        }
+
+        return groupKey;
+    }
+
+    protected String check(final RelationshipCond cond) {
+        String rightAnyObjectKey;
+        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getAnyObject()).matches()) {
+            rightAnyObjectKey = cond.getAnyObject();
+        } else {
+            AnyObject anyObject = anyObjectDAO.findByName(cond.getAnyObject());
+            rightAnyObjectKey = anyObject == null ? null : anyObject.getKey();
+        }
+        if (rightAnyObjectKey == null) {
+            LOG.error("Could not find any object for '" + cond.getAnyObject() + "'");
+            throw new IllegalArgumentException();
+        }
+
+        return rightAnyObjectKey;
+    }
+
+    protected Realm check(final AssignableCond cond) {
+        Realm realm = realmDAO.findByFullPath(cond.getRealmFullPath());
+        if (realm == null) {
+            LOG.error("Could not find realm for '" + cond.getRealmFullPath() + "'");
+            throw new IllegalArgumentException();
+        }
+
+        return realm;
+    }
+
+    protected String check(final MemberCond cond) {
+        String memberKey;
+        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getMember()).matches()) {
+            memberKey = cond.getMember();
+        } else {
+            Any<?> member = userDAO.findByUsername(cond.getMember());
+            if (member == null) {
+                member = anyObjectDAO.findByName(cond.getMember());
+            }
+            memberKey = member == null ? null : member.getKey();
+        }
+        if (memberKey == null) {
+            LOG.error("Could not find user or any object for '" + cond.getMember() + "'");
+            throw new IllegalArgumentException();
+        }
+
+        return memberKey;
+    }
+
+    protected <T extends Any<?>> List<T> buildResult(final List<Object> raw, final AnyTypeKind kind) {
+        List<T> result = new ArrayList<>();
+
+        for (Object anyKey : raw) {
+            String actualKey = anyKey instanceof Object[]
+                    ? (String) ((Object[]) anyKey)[0]
+                    : ((String) anyKey);
+
+            @SuppressWarnings("unchecked")
+            T any = kind == AnyTypeKind.USER
+                    ? (T) userDAO.find(actualKey)
+                    : kind == AnyTypeKind.GROUP
+                            ? (T) groupDAO.find(actualKey)
+                            : (T) anyObjectDAO.find(actualKey);
+            if (any == null) {
+                LOG.error("Could not find {} with id {}, even if returned by native query", kind, actualKey);
+            } else if (!result.contains(any)) {
+                result.add(any);
+            }
+        }
+
+        return result;
+    }
+
+    @Override
+    public <T extends Any<?>> List<T> search(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final int page,
+            final int itemsPerPage,
+            final List<OrderByClause> orderBy,
+            final AnyTypeKind kind) {
+
+        if (adminRealms == null || adminRealms.isEmpty()) {
+            LOG.error("No realms provided");
+            return Collections.<T>emptyList();
+        }
+
+        LOG.debug("Search condition:\n{}", cond);
+        if (cond == null || !cond.isValid()) {
+            LOG.error("Invalid search condition:\n{}", cond);
+            return Collections.<T>emptyList();
+        }
+
+        return doSearch(adminRealms, cond, page, itemsPerPage, orderBy, kind);
+    }
+
+    @Override
+    public <T extends Any<?>> boolean matches(final T any, final SearchCond cond) {
+        return search(cond, any.getType().getKind()).contains(any);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
index 164f9a3..a64afe3 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnyObjectDAO.java
@@ -59,6 +59,8 @@ import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject;
 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.event.AnyCreatedUpdatedEvent;
+import org.apache.syncope.core.spring.event.AnyDeletedEvent;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
@@ -175,21 +177,28 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     }
 
     @Override
-    public List<ARelationship> findARelationships(final AnyObject anyObject) {
+    public List<ARelationship> findAllARelationships(final AnyObject anyObject) {
         TypedQuery<ARelationship> query = entityManager().createQuery(
                 "SELECT e FROM " + JPAARelationship.class.getSimpleName()
-                + " e WHERE e.rightEnd=:anyObject", ARelationship.class);
+                + " e WHERE e.rightEnd=:anyObject OR e.leftEnd=:anyObject", ARelationship.class);
         query.setParameter("anyObject", anyObject);
 
         return query.getResultList();
     }
 
     @Override
-    public List<URelationship> findURelationships(final AnyObject anyObject) {
-        TypedQuery<URelationship> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAURelationship.class.getSimpleName()
-                + " e WHERE e.rightEnd=:anyObject", URelationship.class);
-        query.setParameter("anyObject", anyObject);
+    public int count() {
+        Query query = entityManager().createQuery(
+                "SELECT COUNT(e) FROM  " + JPAAnyObject.class.getSimpleName() + " e");
+        return ((Number) query.getSingleResult()).intValue();
+    }
+
+    @Override
+    public List<AnyObject> findAll(final int page, final int itemsPerPage) {
+        TypedQuery<AnyObject> query = entityManager().createQuery(
+                "SELECT e FROM  " + JPAAnyObject.class.getSimpleName() + " e", AnyObject.class);
+        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+        query.setMaxResults(itemsPerPage);
 
         return query.getResultList();
     }
@@ -197,12 +206,31 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
     @Override
     public AnyObject save(final AnyObject anyObject) {
         AnyObject merged = super.save(anyObject);
+        publisher.publishEvent(new AnyCreatedUpdatedEvent<>(this, merged));
 
         groupDAO().refreshDynMemberships(merged);
 
         return merged;
     }
 
+    private List<ARelationship> findARelationships(final AnyObject anyObject) {
+        TypedQuery<ARelationship> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAARelationship.class.getSimpleName()
+                + " e WHERE e.rightEnd=:anyObject", ARelationship.class);
+        query.setParameter("anyObject", anyObject);
+
+        return query.getResultList();
+    }
+
+    private List<URelationship> findURelationships(final AnyObject anyObject) {
+        TypedQuery<URelationship> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAURelationship.class.getSimpleName()
+                + " e WHERE e.rightEnd=:anyObject", URelationship.class);
+        query.setParameter("anyObject", anyObject);
+
+        return query.getResultList();
+    }
+
     @Override
     public void delete(final AnyObject any) {
         for (Group group : findDynGroupMemberships(any)) {
@@ -223,6 +251,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
         }
 
         entityManager().remove(any);
+        publisher.publishEvent(new AnyDeletedEvent(this, AnyTypeKind.ANY_OBJECT, any.getKey()));
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
@@ -287,7 +316,7 @@ public class JPAAnyObjectDAO extends AbstractAnyDAO<AnyObject> implements AnyObj
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
-    public Collection<String> findAllResourceNames(final String key) {
+    public Collection<String> findAllResourceKeys(final String key) {
         return CollectionUtils.collect(findAllResources(authFind(key)), EntityUtils.<ExternalResource>keyTransformer());
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 49bf4ff..276b570 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -18,36 +18,23 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
-import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import javax.persistence.Entity;
 import javax.persistence.Query;
 import javax.persistence.TemporalType;
-import javax.validation.ValidationException;
-import javax.validation.constraints.Max;
-import javax.validation.constraints.Min;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.ClassUtils;
-import org.apache.commons.lang3.SerializationUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
 import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
-import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
-import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -62,42 +49,18 @@ import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 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.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-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.persistence.jpa.entity.JPAPlainSchema;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Propagation;
-import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.ReflectionUtils;
 
-@Repository
-public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO {
+/**
+ * Search engine implementation for users, groups and any objects, based on self-updating SQL views.
+ */
+public class JPAAnySearchDAO extends AbstractAnySearchDAO {
 
     private static final String EMPTY_QUERY = "SELECT any_id FROM user_search_attr WHERE 1=2";
 
-    @Autowired
-    private RealmDAO realmDAO;
-
-    @Autowired
-    private AnyObjectDAO anyObjectDAO;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private GroupDAO groupDAO;
-
-    @Autowired
-    private PlainSchemaDAO schemaDAO;
-
-    @Autowired
-    private AnyUtilsFactory anyUtilsFactory;
-
     private String getAdminRealmsFilter(
             final Set<String> adminRealms,
             final SearchSupport svs,
@@ -135,11 +98,11 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
     }
 
     @Override
-    public int count(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind typeKind) {
+    protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
         List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
 
         // 1. get the query string from the search condition
-        SearchSupport svs = new SearchSupport(typeKind);
+        SearchSupport svs = new SearchSupport(kind);
         StringBuilder queryString = getQuery(cond, parameters, svs);
 
         // 2. take into account administrative realms
@@ -156,78 +119,56 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
         return ((Number) countQuery.getSingleResult()).intValue();
     }
 
-    @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
-    @Override
-    public <T extends Any<?>> List<T> searchAssignable(final String realmFullPath, final AnyTypeKind kind) {
-        AssignableCond assignableCond = new AssignableCond();
-        assignableCond.setRealmFullPath(realmFullPath);
-        return search(SearchCond.getLeafCond(assignableCond), kind);
-    }
-
-    @Override
-    public <T extends Any<?>> List<T> search(final SearchCond cond, final AnyTypeKind typeKind) {
-        return search(cond, Collections.<OrderByClause>emptyList(), typeKind);
-    }
-
     @Override
-    public <T extends Any<?>> List<T> search(
-            final SearchCond cond, final List<OrderByClause> orderBy, final AnyTypeKind typeKind) {
-
-        return search(SyncopeConstants.FULL_ADMIN_REALMS, cond, -1, -1, orderBy, typeKind);
-    }
-
-    @Override
-    public <T extends Any<?>> List<T> search(
-            final Set<String> adminRealms, final SearchCond cond, final int page, final int itemsPerPage,
-            final List<OrderByClause> orderBy, final AnyTypeKind typeKind) {
+    @SuppressWarnings("unchecked")
+    protected <T extends Any<?>> List<T> doSearch(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final int page,
+            final int itemsPerPage,
+            final List<OrderByClause> orderBy,
+            final AnyTypeKind kind) {
 
-        List<T> result = Collections.<T>emptyList();
+        try {
+            List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
 
-        if (adminRealms != null && !adminRealms.isEmpty()) {
-            LOG.debug("Search condition:\n{}", cond);
+            // 1. get the query string from the search condition
+            SearchSupport svs = new SearchSupport(kind);
+            StringBuilder queryString = getQuery(cond, parameters, svs);
 
-            if (cond != null && cond.isValid()) {
-                try {
-                    result = doSearch(adminRealms, cond, page, itemsPerPage, orderBy, typeKind);
-                } catch (Exception e) {
-                    LOG.error("While searching for {}", typeKind, e);
-                }
+            // 2. take into account realms and ordering
+            OrderBySupport obs = parseOrderBy(kind, svs, orderBy);
+            if (queryString.charAt(0) == '(') {
+                queryString.insert(0, buildSelect(obs));
+                queryString.append(buildWhere(svs, obs));
             } else {
-                LOG.error("Invalid search condition:\n{}", cond);
+                queryString.insert(0, buildSelect(obs).append('('));
+                queryString.append(')').append(buildWhere(svs, obs));
             }
-        }
-
-        return result;
-    }
-
-    @Override
-    public <T extends Any<?>> boolean matches(final T any, final SearchCond cond) {
-        List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
-
-        // 1. get the query string from the search condition
-        SearchSupport svs = new SearchSupport(any.getType().getKind());
-        StringBuilder queryString = getQuery(cond, parameters, svs);
-
-        boolean matches;
-        if (queryString.length() == 0) {
-            // Could be empty: got into a group search with a single membership condition ...
-            matches = false;
-        } else {
-            // 2. take into account the passed user
-            queryString.insert(0, "SELECT u.any_id FROM (");
-            queryString.append(") u WHERE any_id=?").append(setParameter(parameters, any.getKey()));
+            queryString.
+                    append(getAdminRealmsFilter(adminRealms, svs, parameters)).
+                    append(buildOrderBy(obs));
 
             // 3. prepare the search query
             Query query = entityManager().createNativeQuery(queryString.toString());
 
-            // 4. populate the search query with parameter values
+            // 4. page starts from 1, while setFirtResult() starts from 0
+            query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
+
+            if (itemsPerPage >= 0) {
+                query.setMaxResults(itemsPerPage);
+            }
+
+            // 5. populate the search query with parameter values
             fillWithParameters(query, parameters);
 
-            // 5. executes query
-            matches = !query.getResultList().isEmpty();
+            // 6. Prepare the result (avoiding duplicates)
+            return buildResult(query.getResultList(), kind);
+        } catch (Exception e) {
+            LOG.error("While searching for {}", kind, e);
         }
 
-        return matches;
+        return Collections.emptyList();
     }
 
     private int setParameter(final List<Object> parameters, final Object parameter) {
@@ -370,67 +311,6 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
         return obs;
     }
 
-    @SuppressWarnings("unchecked")
-    private <T extends Any<?>> List<T> doSearch(final Set<String> adminRealms,
-            final SearchCond cond, final int page, final int itemsPerPage, final List<OrderByClause> orderBy,
-            final AnyTypeKind typeKind) {
-
-        List<Object> parameters = Collections.synchronizedList(new ArrayList<>());
-
-        // 1. get the query string from the search condition
-        SearchSupport svs = new SearchSupport(typeKind);
-        StringBuilder queryString = getQuery(cond, parameters, svs);
-
-        // 2. take into account administrative groups and ordering
-        OrderBySupport obs = parseOrderBy(typeKind, svs, orderBy);
-        if (queryString.charAt(0) == '(') {
-            queryString.insert(0, buildSelect(obs));
-            queryString.append(buildWhere(svs, obs));
-        } else {
-            queryString.insert(0, buildSelect(obs).append('('));
-            queryString.append(')').append(buildWhere(svs, obs));
-        }
-        queryString.
-                append(getAdminRealmsFilter(adminRealms, svs, parameters)).
-                append(buildOrderBy(obs));
-
-        // 3. prepare the search query
-        Query query = entityManager().createNativeQuery(queryString.toString());
-
-        // 4. page starts from 1, while setFirtResult() starts from 0
-        query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
-
-        if (itemsPerPage >= 0) {
-            query.setMaxResults(itemsPerPage);
-        }
-
-        // 5. populate the search query with parameter values
-        fillWithParameters(query, parameters);
-
-        // 6. Prepare the result (avoiding duplicates)
-        List<T> result = new ArrayList<>();
-
-        for (Object anyKey : query.getResultList()) {
-            String actualKey = anyKey instanceof Object[]
-                    ? (String) ((Object[]) anyKey)[0]
-                    : ((String) anyKey);
-
-            T any = typeKind == AnyTypeKind.USER
-                    ? (T) userDAO.find(actualKey)
-                    : typeKind == AnyTypeKind.GROUP
-                            ? (T) groupDAO.find(actualKey)
-                            : (T) anyObjectDAO.find(actualKey);
-            if (any == null) {
-                LOG.error("Could not find {} with id {}, even though returned by the native query",
-                        typeKind, actualKey);
-            } else if (!result.contains(any)) {
-                result.add(any);
-            }
-        }
-
-        return result;
-    }
-
     private StringBuilder getQuery(final SearchCond cond, final List<Object> parameters, final SearchSupport svs) {
         StringBuilder query = new StringBuilder();
 
@@ -548,13 +428,9 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
             final RelationshipCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
         String rightAnyObjectKey;
-        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getAnyObject()).matches()) {
-            rightAnyObjectKey = cond.getAnyObject();
-        } else {
-            AnyObject anyObject = anyObjectDAO.findByName(cond.getAnyObject());
-            rightAnyObjectKey = anyObject == null ? null : anyObject.getKey();
-        }
-        if (rightAnyObjectKey == null) {
+        try {
+            rightAnyObjectKey = check(cond);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -579,13 +455,9 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
             final MembershipCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
         String groupKey;
-        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getGroup()).matches()) {
-            groupKey = cond.getGroup();
-        } else {
-            Group group = groupDAO.findByName(cond.getGroup());
-            groupKey = group == null ? null : group.getKey();
-        }
-        if (groupKey == null) {
+        try {
+            groupKey = check(cond);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -678,8 +550,10 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
     }
 
     private String getQuery(final AssignableCond cond, final List<Object> parameters, final SearchSupport svs) {
-        Realm realm = realmDAO.findByFullPath(cond.getRealmFullPath());
-        if (realm == null) {
+        Realm realm;
+        try {
+            realm = check(cond);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -705,16 +579,9 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
             final MemberCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
         String memberKey;
-        if (SyncopeConstants.UUID_PATTERN.matcher(cond.getMember()).matches()) {
-            memberKey = cond.getMember();
-        } else {
-            Any<?> member = userDAO.findByUsername(cond.getMember());
-            if (member == null) {
-                member = anyObjectDAO.findByName(cond.getMember());
-            }
-            memberKey = member == null ? null : member.getKey();
-        }
-        if (memberKey == null) {
+        try {
+            memberKey = check(cond);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -746,7 +613,7 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
         return query.toString();
     }
 
-    private void fillAttributeQuery(
+    private void fillAttrQuery(
             final StringBuilder query,
             final PlainAttrValue attrValue,
             final PlainSchema schema,
@@ -864,28 +731,10 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
     private String getQuery(
             final AttributeCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
-        AnyUtils attrUtils = anyUtilsFactory.getInstance(svs.anyTypeKind);
-
-        PlainSchema schema = schemaDAO.find(cond.getSchema());
-        if (schema == null) {
-            LOG.warn("Ignoring invalid schema '{}'", cond.getSchema());
-            return EMPTY_QUERY;
-        }
-
-        // keep track of involvement of non-mandatory schemas in the search condition
-        svs.nonMandatorySchemas = !"true".equals(schema.getMandatoryCondition());
-
-        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
+        Pair<PlainSchema, PlainAttrValue> checked;
         try {
-            if (cond.getType() != AttributeCond.Type.LIKE
-                    && cond.getType() != AttributeCond.Type.ILIKE
-                    && cond.getType() != AttributeCond.Type.ISNULL
-                    && cond.getType() != AttributeCond.Type.ISNOTNULL) {
-
-                schema.getValidator().validate(cond.getExpression(), attrValue);
-            }
-        } catch (ValidationException e) {
-            LOG.error("Could not validate expression '" + cond.getExpression() + "'", e);
+            checked = check(cond, svs.anyTypeKind);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
@@ -895,22 +744,22 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
                 query.append(svs.field().name).
                         append(" WHERE any_id NOT IN (SELECT any_id FROM ").
                         append(svs.nullAttr().name).
-                        append(" WHERE schema_id='").append(schema.getKey()).append("')");
+                        append(" WHERE schema_id='").append(checked.getLeft().getKey()).append("')");
                 break;
 
             case ISNULL:
                 query.append(svs.nullAttr().name).
-                        append(" WHERE schema_id='").append(schema.getKey()).append("'");
+                        append(" WHERE schema_id='").append(checked.getLeft().getKey()).append("'");
                 break;
 
             default:
-                if (schema.isUniqueConstraint()) {
+                if (checked.getLeft().isUniqueConstraint()) {
                     query.append(svs.uniqueAttr().name);
                 } else {
                     query.append(svs.attr().name);
                 }
-                query.append(" WHERE schema_id='").append(schema.getKey());
-                fillAttributeQuery(query, attrValue, schema, cond, not, parameters, svs);
+                query.append(" WHERE schema_id='").append(checked.getLeft().getKey());
+                fillAttrQuery(query, checked.getRight(), checked.getLeft(), cond, not, parameters, svs);
         }
 
         return query.toString();
@@ -919,78 +768,17 @@ public class JPAAnySearchDAO extends AbstractDAO<Any<?>> implements AnySearchDAO
     private String getQuery(
             final AnyCond cond, final boolean not, final List<Object> parameters, final SearchSupport svs) {
 
-        AnyCond condClone = SerializationUtils.clone(cond);
-
-        AnyUtils attrUtils = anyUtilsFactory.getInstance(svs.anyTypeKind);
-
-        // Keeps track of difference between entity's getKey() and JPA @Id fields
-        if ("key".equals(condClone.getSchema())) {
-            condClone.setSchema("id");
-        }
-
-        Field anyField = ReflectionUtils.findField(attrUtils.anyClass(), condClone.getSchema());
-        if (anyField == null) {
-            LOG.warn("Ignoring invalid schema '{}'", condClone.getSchema());
+        Triple<PlainSchema, PlainAttrValue, AnyCond> checked;
+        try {
+            checked = check(cond, svs.anyTypeKind);
+        } catch (IllegalArgumentException e) {
             return EMPTY_QUERY;
         }
 
-        PlainSchema schema = new JPAPlainSchema();
-        schema.setKey(anyField.getName());
-        for (AttrSchemaType attrSchemaType : AttrSchemaType.values()) {
-            if (anyField.getType().isAssignableFrom(attrSchemaType.getType())) {
-                schema.setType(attrSchemaType);
-            }
-        }
-
-        // Deal with any Integer fields logically mapping to boolean values
-        boolean foundBooleanMin = false;
-        boolean foundBooleanMax = false;
-        if (Integer.class.equals(anyField.getType())) {
-            for (Annotation annotation : anyField.getAnnotations()) {
-                if (Min.class.equals(annotation.annotationType())) {
-                    foundBooleanMin = ((Min) annotation).value() == 0;
-                } else if (Max.class.equals(annotation.annotationType())) {
-                    foundBooleanMax = ((Max) annotation).value() == 1;
-                }
-            }
-        }
-        if (foundBooleanMin && foundBooleanMax) {
-            schema.setType(AttrSchemaType.Boolean);
-        }
-
-        // Deal with any fields representing relationships to other entities
-        if (anyField.getType().getAnnotation(Entity.class) != null) {
-            Method relMethod = null;
-            try {
-                relMethod = ClassUtils.getPublicMethod(anyField.getType(), "getKey", new Class<?>[0]);
-            } catch (Exception e) {
-                LOG.error("Could not find {}#getKey", anyField.getType(), e);
-            }
-
-            if (relMethod != null && String.class.isAssignableFrom(relMethod.getReturnType())) {
-                condClone.setSchema(condClone.getSchema() + "_id");
-                schema.setType(AttrSchemaType.String);
-            }
-        }
-
-        PlainAttrValue attrValue = attrUtils.newPlainAttrValue();
-        if (condClone.getType() != AttributeCond.Type.LIKE
-                && condClone.getType() != AttributeCond.Type.ILIKE
-                && condClone.getType() != AttributeCond.Type.ISNULL
-                && condClone.getType() != AttributeCond.Type.ISNOTNULL) {
-
-            try {
-                schema.getValidator().validate(condClone.getExpression(), attrValue);
-            } catch (ValidationException e) {
-                LOG.error("Could not validate expression '" + condClone.getExpression() + "'", e);
-                return EMPTY_QUERY;
-            }
-        }
-
         StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
                 append(svs.field().name).append(" WHERE ");
 
-        fillAttributeQuery(query, attrValue, schema, condClone, not, parameters, svs);
+        fillAttrQuery(query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, parameters, svs);
 
         return query.toString();
     }