[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(); }