[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ò <ilgro...@apache.org>
Authored: Mon May 8 13:32:27 2017 +0200
Committer: Francesco Chicchiriccò <ilgro...@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();
     }

Reply via email to