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

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
new file mode 100644
index 0000000..572ef37
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.ext.elasticsearch.client;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
+import org.apache.syncope.core.persistence.api.entity.anyobject.ARelationship;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership;
+import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.URelationship;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.utils.EntityUtils;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Utility methods for usage with Elasticsearch.
+ */
+public class ElasticsearchUtils {
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnyObjectDAO anyObjectDAO;
+
+    private int indexMaxResultWindow = 10000;
+
+    public void setIndexMaxResultWindow(final int indexMaxResultWindow) {
+        this.indexMaxResultWindow = indexMaxResultWindow;
+    }
+
+    public int getIndexMaxResultWindow() {
+        return indexMaxResultWindow;
+    }
+
+    /**
+     * Returns the builder specialized with content from the provided any.
+     *
+     * @param any user, group or any object to index
+     * @return builder specialized with content from the provided any
+     * @throws IOException in case of errors
+     */
+    @Transactional
+    public XContentBuilder builder(final Any<?> any) throws IOException {
+        XContentBuilder builder = XContentFactory.jsonBuilder().
+                startObject().
+                field("id", any.getKey()).
+                field("realm", any.getRealm().getFullPath()).
+                field("anyType", any.getType().getKey()).
+                field("creationDate", any.getCreationDate()).
+                field("creator", any.getCreator()).
+                field("lastChangeDate", any.getLastChangeDate()).
+                field("lastModified", any.getLastModifier()).
+                field("status", any.getStatus()).
+                field("resources",
+                        any instanceof User
+                                ? userDAO.findAllResourceKeys(any.getKey())
+                                : any instanceof AnyObject
+                                        ? anyObjectDAO.findAllResourceKeys(any.getKey())
+                                        : any.getResourceKeys());
+
+        if (any instanceof AnyObject) {
+            AnyObject anyObject = ((AnyObject) any);
+            builder = builder.field("name", anyObject.getName());
+
+            List<Object> memberships = new ArrayList<Object>(anyObjectDAO.findAllGroupKeys(anyObject));
+            builder = builder.field("memberships", memberships);
+
+            List<Object> relationships = new ArrayList<>();
+            List<Object> relationshipTypes = new ArrayList<>();
+            for (ARelationship relationship : anyObjectDAO.findAllARelationships(anyObject)) {
+                relationships.add(relationship.getRightEnd().getKey());
+                relationshipTypes.add(relationship.getType().getKey());
+            }
+            builder = builder.field("relationships", relationships);
+            builder = builder.field("relationshipTypes", relationshipTypes);
+        } else if (any instanceof Group) {
+            Group group = ((Group) any);
+            builder = builder.field("name", group.getName());
+            if (group.getUserOwner() != null) {
+                builder = builder.field("userOwner", group.getUserOwner().getKey());
+            }
+            if (group.getGroupOwner() != null) {
+                builder = builder.field("groupOwner", group.getGroupOwner().getKey());
+            }
+
+            List<Object> members = CollectionUtils.collect(groupDAO.findUMemberships(group),
+                    new Transformer<UMembership, Object>() {
+
+                @Override
+                public Object transform(final UMembership input) {
+                    return input.getLeftEnd().getKey();
+                }
+            }, new ArrayList<>());
+            UDynGroupMembership udynmembership = group.getUDynMembership();
+            if (udynmembership != null) {
+                CollectionUtils.collect(udynmembership.getMembers(), EntityUtils.keyTransformer(), members);
+            }
+            CollectionUtils.collect(groupDAO.findAMemberships(group),
+                    new Transformer<AMembership, Object>() {
+
+                @Override
+                public Object transform(final AMembership input) {
+                    return input.getLeftEnd().getKey();
+                }
+            }, members);
+            for (ADynGroupMembership adynmembership : group.getADynMemberships()) {
+                CollectionUtils.collect(adynmembership.getMembers(), EntityUtils.keyTransformer(), members);
+            }
+            builder = builder.field("members", members);
+        } else if (any instanceof User) {
+            User user = ((User) any);
+            builder = builder.
+                    field("username", user.getUsername()).
+                    field("lastLoginDate", user.getLastLoginDate()).
+                    field("lastRecertification", user.getLastRecertification()).
+                    field("lastRecertificator", user.getLastRecertificator());
+
+            List<Object> roles = CollectionUtils.collect(userDAO.findAllRoles(user),
+                    EntityUtils.<Role>keyTransformer(), new ArrayList<>());
+            builder = builder.field("roles", roles);
+
+            List<Object> memberships = new ArrayList<Object>(userDAO.findAllGroupKeys(user));
+            builder = builder.field("memberships", memberships);
+
+            List<Object> relationships = new ArrayList<>();
+            Set<Object> relationshipTypes = new HashSet<>();
+            for (URelationship relationship : user.getRelationships()) {
+                relationships.add(relationship.getRightEnd().getKey());
+                relationshipTypes.add(relationship.getType().getKey());
+            }
+            builder = builder.field("relationships", relationships);
+            builder = builder.field("relationshipTypes", relationshipTypes);
+        }
+
+        for (PlainAttr<?> plainAttr : any.getPlainAttrs()) {
+            List<Object> values = CollectionUtils.collect(plainAttr.getValues(),
+                    new Transformer<PlainAttrValue, Object>() {
+
+                @Override
+                public Object transform(final PlainAttrValue input) {
+                    return input.getValue();
+                }
+            }, new ArrayList<>(plainAttr.getValues().size()));
+            if (plainAttr.getUniqueValue() != null) {
+                values.add(plainAttr.getUniqueValue().getValue());
+            }
+
+            builder = builder.field(plainAttr.getSchema().getKey(), values);
+        }
+
+        builder = builder.endObject();
+
+        return builder;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml b/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
new file mode 100644
index 0000000..f870317
--- /dev/null
+++ b/ext/elasticsearch/client-elasticsearch/src/main/resources/elasticsearchClientContext.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchClientFactoryBean">
+    <property name="settings">
+      <map>
+        <entry key="cluster.name">
+          <value>elasticsearch</value>
+        </entry>
+        <entry key="client.transport.sniff">
+          <value>true</value>
+        </entry>
+      </map>
+    </property>  
+    <property name="addresses">
+      <map>
+        <entry key="localhost">
+          <value>9300</value>
+        </entry>
+      </map>
+    </property>
+  </bean>
+  
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils">
+    <property name="indexMaxResultWindow" value="10000"/>
+  </bean>
+  <bean class="org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager"/>
+  
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/persistence-jpa/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/pom.xml b/ext/elasticsearch/persistence-jpa/pom.xml
new file mode 100644
index 0000000..129b36b
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-elasticsearch</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch Persistence JPA</name>
+  <description>Apache Syncope Ext: Elasticsearch Persistence JPA</description>
+  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+  <artifactId>syncope-ext-elasticsearch-persistence-jpa</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>    
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-persistence-jpa</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+      <artifactId>syncope-ext-elasticsearch-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
new file mode 100644
index 0000000..34651e1
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
@@ -0,0 +1,355 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.dao;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.commons.lang3.tuple.Triple;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
+import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
+import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
+import org.apache.syncope.core.persistence.api.dao.search.ResourceCond;
+import org.apache.syncope.core.persistence.api.dao.search.RoleCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchType;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.index.query.DisMaxQueryBuilder;
+import org.elasticsearch.index.query.MatchNoneQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Search engine implementation for users, groups and any objects, based on Elasticsearch.
+ */
+public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
+
+    private static final QueryBuilder EMPTY_QUERY_BUILDER = new MatchNoneQueryBuilder();
+
+    @Autowired
+    private Client client;
+
+    @Autowired
+    private ElasticsearchUtils elasticsearchUtils;
+
+    private DisMaxQueryBuilder adminRealmsFilter(final Set<String> adminRealms) {
+        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
+
+        for (String realmPath : RealmUtils.normalize(adminRealms)) {
+            Realm realm = realmDAO.findByFullPath(realmPath);
+            if (realm == null) {
+                LOG.warn("Ignoring invalid realm {}", realmPath);
+            } else {
+                for (Realm descendant : realmDAO.findDescendants(realm)) {
+                    builder.add(QueryBuilders.termQuery("realm.keyword", descendant.getFullPath()));
+                }
+            }
+        }
+
+        return builder;
+    }
+
+    private SearchRequestBuilder searchRequestBuilder(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final AnyTypeKind kind) {
+
+        return client.prepareSearch(AuthContextUtils.getDomain().toLowerCase()).
+                setTypes(kind.name()).
+                setSearchType(SearchType.QUERY_THEN_FETCH).
+                setQuery(SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms)
+                        ? getQueryBuilder(cond, kind)
+                        : QueryBuilders.boolQuery().
+                                must(adminRealmsFilter(adminRealms)).
+                                must(getQueryBuilder(cond, kind)));
+    }
+
+    @Override
+    protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
+        SearchRequestBuilder builder = searchRequestBuilder(adminRealms, cond, kind).
+                setFrom(0).setSize(0);
+
+        return (int) builder.get().getHits().getTotalHits();
+    }
+
+    @Override
+    protected <T extends Any<?>> List<T> doSearch(
+            final Set<String> adminRealms,
+            final SearchCond cond,
+            final int page,
+            final int itemsPerPage,
+            final List<OrderByClause> orderBy,
+            final AnyTypeKind kind) {
+
+        SearchRequestBuilder builder = searchRequestBuilder(adminRealms, cond, kind).
+                setFrom(page <= 0 ? 0 : page - 1).
+                setSize(itemsPerPage < 0 ? elasticsearchUtils.getIndexMaxResultWindow() : itemsPerPage);
+
+        return buildResult(
+                CollectionUtils.collect(Arrays.asList(builder.get().getHits().getHits()),
+                        new Transformer<SearchHit, Object>() {
+
+                    @Override
+                    public Object transform(final SearchHit input) {
+                        return input.getId();
+                    }
+                }, new ArrayList<>()),
+                kind);
+    }
+
+    private QueryBuilder getQueryBuilder(final SearchCond cond, final AnyTypeKind kind) {
+        QueryBuilder builder = EMPTY_QUERY_BUILDER;
+
+        switch (cond.getType()) {
+            case LEAF:
+            case NOT_LEAF:
+                if (cond.getAnyTypeCond() != null && AnyTypeKind.ANY_OBJECT == kind) {
+                    builder = getQueryBuilder(cond.getAnyTypeCond());
+                } else if (cond.getRelationshipTypeCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getRelationshipTypeCond());
+                } else if (cond.getRelationshipCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getRelationshipCond());
+                } else if (cond.getMembershipCond() != null
+                        && (AnyTypeKind.USER == kind || AnyTypeKind.ANY_OBJECT == kind)) {
+
+                    builder = getQueryBuilder(cond.getMembershipCond());
+                } else if (cond.getAssignableCond() != null) {
+                    builder = getQueryBuilder(cond.getAssignableCond());
+                } else if (cond.getRoleCond() != null && AnyTypeKind.USER == kind) {
+                    builder = getQueryBuilder(cond.getRoleCond());
+                } else if (cond.getMemberCond() != null && AnyTypeKind.GROUP == kind) {
+                    builder = getQueryBuilder(cond.getMemberCond());
+                } else if (cond.getResourceCond() != null) {
+                    builder = getQueryBuilder(cond.getResourceCond());
+                } else if (cond.getAttributeCond() != null) {
+                    builder = getQueryBuilder(cond.getAttributeCond(), kind);
+                } else if (cond.getAnyCond() != null) {
+                    builder = getQueryBuilder(cond.getAnyCond(), kind);
+                }
+                builder = checkNot(builder, cond.getType() == SearchCond.Type.NOT_LEAF);
+                break;
+
+            case AND:
+                builder = QueryBuilders.boolQuery().
+                        must(getQueryBuilder(cond.getLeftSearchCond(), kind)).
+                        must(getQueryBuilder(cond.getRightSearchCond(), kind));
+                break;
+
+            case OR:
+                builder = QueryBuilders.disMaxQuery().
+                        add(getQueryBuilder(cond.getLeftSearchCond(), kind)).
+                        add(getQueryBuilder(cond.getRightSearchCond(), kind));
+                break;
+
+            default:
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder checkNot(final QueryBuilder builder, final boolean not) {
+        return not
+                ? QueryBuilders.boolQuery().mustNot(builder)
+                : builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final AnyTypeCond cond) {
+        return QueryBuilders.termQuery("anyType.keyword", cond.getAnyTypeKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final RelationshipTypeCond cond) {
+        return QueryBuilders.termQuery("relationshipTypes.keyword", cond.getRelationshipTypeKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final RelationshipCond cond) {
+        String rightAnyObjectKey;
+        try {
+            rightAnyObjectKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("relationships.keyword", rightAnyObjectKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final MembershipCond cond) {
+        String groupKey;
+        try {
+            groupKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("memberships.keyword", groupKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final AssignableCond cond) {
+        Realm realm;
+        try {
+            realm = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
+        if (cond.isFromGroup()) {
+            for (Realm current = realm; current.getParent() != null; current = current.getParent()) {
+                builder.add(QueryBuilders.termQuery("realm.keyword", current.getFullPath()));
+            }
+            builder.add(QueryBuilders.termQuery("realm.keyword", realmDAO.getRoot().getFullPath()));
+        } else {
+            for (Realm current : realmDAO.findDescendants(realm)) {
+                builder.add(QueryBuilders.termQuery("realm.keyword", current.getFullPath()));
+            }
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final RoleCond cond) {
+        return QueryBuilders.termQuery("roles.keyword", cond.getRoleKey());
+    }
+
+    private QueryBuilder getQueryBuilder(final MemberCond cond) {
+        String memberKey;
+        try {
+            memberKey = check(cond);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return QueryBuilders.termQuery("members.keyword", memberKey);
+    }
+
+    private QueryBuilder getQueryBuilder(final ResourceCond cond) {
+        return QueryBuilders.termQuery("resources.keyword", cond.getResourceKey());
+    }
+
+    private QueryBuilder fillAttrQuery(
+            final PlainSchema schema,
+            final PlainAttrValue attrValue,
+            final AttributeCond cond) {
+
+        String name = schema.getType() == AttrSchemaType.String
+                || schema.getType() == AttrSchemaType.Enum
+                ? schema.getKey() + ".keyword"
+                : schema.getKey();
+        Object value = schema.getType() == AttrSchemaType.Date && attrValue.getDateValue() != null
+                ? attrValue.getDateValue().getTime()
+                : attrValue.getValue();
+
+        QueryBuilder builder = EMPTY_QUERY_BUILDER;
+
+        switch (cond.getType()) {
+            case ISNOTNULL:
+                builder = QueryBuilders.existsQuery(name);
+                break;
+
+            case ISNULL:
+                builder = QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(name));
+                break;
+
+            case ILIKE:
+                builder = QueryBuilders.queryStringQuery(
+                        schema.getKey() + ":" + cond.getExpression().replace('%', '*'));
+                break;
+
+            case LIKE:
+                builder = QueryBuilders.wildcardQuery(name, cond.getExpression().replace('%', '*'));
+                break;
+
+            case IEQ:
+                builder = QueryBuilders.matchQuery(schema.getKey(), value);
+                break;
+
+            case EQ:
+                builder = QueryBuilders.termQuery(name, value);
+                break;
+
+            case GE:
+                builder = QueryBuilders.rangeQuery(name).gte(value);
+                break;
+
+            case GT:
+                builder = QueryBuilders.rangeQuery(name).gt(value);
+                break;
+
+            case LE:
+                builder = QueryBuilders.rangeQuery(name).lte(value);
+                break;
+
+            case LT:
+                builder = QueryBuilders.rangeQuery(name).lt(value);
+                break;
+
+            default:
+        }
+
+        return builder;
+    }
+
+    private QueryBuilder getQueryBuilder(final AttributeCond cond, final AnyTypeKind kind) {
+        Pair<PlainSchema, PlainAttrValue> checked;
+        try {
+            checked = check(cond, kind);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return fillAttrQuery(checked.getLeft(), checked.getRight(), cond);
+    }
+
+    private QueryBuilder getQueryBuilder(final AnyCond cond, final AnyTypeKind kind) {
+        Triple<PlainSchema, PlainAttrValue, AnyCond> checked;
+        try {
+            checked = check(cond, kind);
+        } catch (IllegalArgumentException e) {
+            return EMPTY_QUERY_BUILDER;
+        }
+
+        return fillAttrQuery(checked.getLeft(), checked.getMiddle(), checked.getRight());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
new file mode 100644
index 0000000..6b8f4aa
--- /dev/null
+++ b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+content.directory=${conf.directory}
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/pom.xml b/ext/elasticsearch/pom.xml
new file mode 100644
index 0000000..0bbb7b3
--- /dev/null
+++ b/ext/elasticsearch/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-ext</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch</name>
+  <description>Apache Syncope Ext: Elasticsearch</description>
+  <groupId>org.apache.syncope.ext</groupId>
+  <artifactId>syncope-ext-elasticsearch</artifactId>
+  <packaging>pom</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+  </properties>
+  
+  <modules>
+    <module>client-elasticsearch</module>
+    <module>persistence-jpa</module>
+    <module>provisioning-java</module>
+  </modules>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/provisioning-java/pom.xml b/ext/elasticsearch/provisioning-java/pom.xml
new file mode 100644
index 0000000..7108905
--- /dev/null
+++ b/ext/elasticsearch/provisioning-java/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.ext</groupId>
+    <artifactId>syncope-ext-elasticsearch</artifactId>
+    <version>2.0.4-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Ext: Elasticsearch Provisioning Java</name>
+  <description>Apache Syncope Ext: Elasticsearch Provisioning Java</description>
+  <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+  <artifactId>syncope-ext-elasticsearch-provisioning-java</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-provisioning-java</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.ext.elasticsearch</groupId>
+      <artifactId>syncope-ext-elasticsearch-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
----------------------------------------------------------------------
diff --git a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
new file mode 100644
index 0000000..b9d8b15
--- /dev/null
+++ b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.job;
+
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.AnyDAO;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
+import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
+import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
+import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
+import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
+import org.elasticsearch.action.index.IndexResponse;
+import org.elasticsearch.client.Client;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * Remove and rebuild all Elasticsearch indexes with information from existing users, groups and any objects.
+ */
+public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
+
+    @Autowired
+    private Client client;
+
+    @Autowired
+    private ElasticsearchUtils elasticsearchUtils;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    @Autowired
+    private AnyObjectDAO anyObjectDAO;
+
+    @Override
+    protected String doExecute(final boolean dryRun) throws JobExecutionException {
+        if (!dryRun) {
+            try {
+                LOG.debug("Start rebuild index {}", AuthContextUtils.getDomain().toLowerCase());
+
+                IndicesExistsResponse existsIndexResponse = client.admin().indices().
+                        exists(new IndicesExistsRequest(AuthContextUtils.getDomain().toLowerCase())).
+                        get();
+                if (existsIndexResponse.isExists()) {
+                    DeleteIndexResponse deleteIndexResponse = client.admin().indices().
+                            delete(new DeleteIndexRequest(AuthContextUtils.getDomain().toLowerCase())).
+                            get();
+                    LOG.debug("Successfully removed {}: {}",
+                            AuthContextUtils.getDomain().toLowerCase(), deleteIndexResponse);
+                }
+
+                CreateIndexResponse createIndexResponse = client.admin().indices().
+                        create(new CreateIndexRequest(AuthContextUtils.getDomain().toLowerCase())).
+                        get();
+                LOG.debug("Successfully created {}: {}",
+                        AuthContextUtils.getDomain().toLowerCase(), createIndexResponse);
+
+                LOG.debug("Indexing users...");
+                for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (User user : userDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.USER.name(),
+                                user.getKey()).
+                                setSource(elasticsearchUtils.builder(user)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", user, response);
+                    }
+                }
+                LOG.debug("Indexing groups...");
+                for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (Group group : groupDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.GROUP.name(),
+                                group.getKey()).
+                                setSource(elasticsearchUtils.builder(group)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", group, response);
+                    }
+                }
+                LOG.debug("Indexing any objects...");
+                for (int page = 1; page <= (anyObjectDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
+                    for (AnyObject anyObject : anyObjectDAO.findAll(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
+                        IndexResponse response = client.prepareIndex(
+                                AuthContextUtils.getDomain().toLowerCase(),
+                                AnyTypeKind.ANY_OBJECT.name(),
+                                anyObject.getKey()).
+                                setSource(elasticsearchUtils.builder(anyObject)).
+                                get();
+                        LOG.debug("Index successfully created for {}: {}", anyObject, response);
+                    }
+                }
+
+                LOG.debug("Rebuild index {} successfully completed", AuthContextUtils.getDomain().toLowerCase());
+            } catch (Exception e) {
+                throw new JobExecutionException(
+                        "While rebuilding index " + AuthContextUtils.getDomain().toLowerCase(), e);
+            }
+        }
+
+        return "SUCCESS";
+    }
+
+    @Override
+    protected boolean hasToBeRegistered(final TaskExec execution) {
+        return true;
+    }
+}

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

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

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
new file mode 100644
index 0000000..6b8f4aa
--- /dev/null
+++ b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+content.directory=${conf.directory}
+any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO

http://git-wip-us.apache.org/repos/asf/syncope/blob/07e31980/fit/core-reference/src/main/resources/elasticsearch/web.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/elasticsearch/web.xml b/fit/core-reference/src/main/resources/elasticsearch/web.xml
new file mode 100644
index 0000000..6d1ea80
--- /dev/null
+++ b/fit/core-reference/src/main/resources/elasticsearch/web.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
+                             http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
+         version="3.1">
+
+  <display-name>Apache Syncope ${syncope.version} Core</display-name>
+
+  <context-param>
+    <param-name>contextConfigLocation</param-name>
+    <param-value>
+      classpath*:/coreContext.xml
+      classpath*:/elasticsearchClientContext.xml
+      classpath*:/securityContext.xml
+      classpath*:/logicContext.xml
+      classpath*:/restCXFContext.xml
+      classpath*:/persistenceContext.xml
+      classpath*:/provisioning*Context.xml
+      classpath*:/workflow*Context.xml
+    </param-value>
+  </context-param>
+
+  <listener>
+    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+  </listener>
+
+  <login-config>
+    <auth-method>CLIENT-CERT</auth-method>
+  </login-config>
+
+</web-app>
\ No newline at end of file

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

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

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