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

[syncope] branch 2_1_X updated (e047ee8 -> 1004d00)

This is an automated email from the ASF dual-hosted git repository.

skylark17 pushed a change to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git.


    from e047ee8  Adding explicit warning about supported Elasticsearch versions
     new ba8338b  Fix documentation about enabling the Elasticsearch extension
     new 1004d00  [SYNCOPE-1399] Added fix for Elasticsearch v6.x

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../client/ElasticsearchIndexManager.java          | 10 +--
 .../elasticsearch/client/ElasticsearchUtils.java   | 11 +++
 .../jpa/dao/ElasticsearchAnySearchDAO.java         |  4 +-
 .../java/job/ElasticsearchReindex.java             | 93 ++++++++++++++++------
 .../syncope/core/logic/init/ElasticsearchInit.java | 71 +++++++++++++++++
 .../fit/core/reference/ITImplementationLookup.java | 38 +--------
 .../reference-guide/concepts/notifications.adoc    |  2 +-
 .../workingwithapachesyncope/customization.adoc    |  9 ++-
 8 files changed, 168 insertions(+), 70 deletions(-)
 create mode 100644 fit/core-reference/src/main/java/org/apache/syncope/core/logic/init/ElasticsearchInit.java


[syncope] 01/02: Fix documentation about enabling the Elasticsearch extension

Posted by sk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

skylark17 pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit ba8338b5e95ec1f2eca33bec78e4a34cc5c53ba3
Author: skylark17 <ma...@tirasa.net>
AuthorDate: Tue Nov 20 10:24:03 2018 +0100

    Fix documentation about enabling the Elasticsearch extension
---
 src/main/asciidoc/reference-guide/concepts/notifications.adoc      | 2 +-
 .../reference-guide/workingwithapachesyncope/customization.adoc    | 7 ++++++-
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/main/asciidoc/reference-guide/concepts/notifications.adoc b/src/main/asciidoc/reference-guide/concepts/notifications.adoc
index eb5653d..6d2d06f 100644
--- a/src/main/asciidoc/reference-guide/concepts/notifications.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/notifications.adoc
@@ -65,7 +65,7 @@ An event is identified by the following five coordinates:
 ** `PUSH`
 ** `CUSTOM`
 . category - the possible values depend on the selected type: for `LOGIC` the <<logic>> components available,
-for `TASK` the various <<tasks-custom>> Tasks configured, for `PROPAGATION`, `PULL` and `PUSH` the defined Any Types
+for `TASK` the various <<tasks-custom, Custom Task>> configured, for `PROPAGATION`, `PULL` and `PUSH` the defined Any Types
 . subcategory - completes category with external resource name, when selecting `PROPAGATION`, `PULL` or `PUSH`
 . event type - the final identification of the event; depends on the other coordinates
 . success or failure - whether the current event shall be considered in case of success or failure
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
index ee7b9cf..ec40b4f 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
@@ -478,7 +478,7 @@ with
 
 ....
 classpath*:/coreContext.xml
-classpath*:/elasticsearchClientContext.xml
+classpath:/elasticsearchClientContext.xml
 classpath*:/securityContext.xml
 classpath*:/logicContext.xml
 classpath*:/restCXFContext.xml
@@ -487,6 +487,11 @@ classpath*:/provisioning*Context.xml
 classpath*:/workflow*Context.xml
 ....
 
+It is also required to initialize the Elasticsearch indexes.
+
+Create a new <<tasks-custom, Custom task>>, specify the 
+`org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex` as job delegate and execute it.
+
 [discrete]
 ===== Enable the <<SCIM>> extension
 


[syncope] 02/02: [SYNCOPE-1399] Added fix for Elasticsearch v6.x

Posted by sk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

skylark17 pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit 1004d00e5621aa65764aa8f0855c9cee6ac12e73
Author: skylark17 <ma...@tirasa.net>
AuthorDate: Tue Nov 20 18:00:24 2018 +0100

    [SYNCOPE-1399] Added fix for Elasticsearch v6.x
---
 .../client/ElasticsearchIndexManager.java          | 10 +--
 .../elasticsearch/client/ElasticsearchUtils.java   | 11 +++
 .../jpa/dao/ElasticsearchAnySearchDAO.java         |  4 +-
 .../java/job/ElasticsearchReindex.java             | 93 ++++++++++++++++------
 .../syncope/core/logic/init/ElasticsearchInit.java | 71 +++++++++++++++++
 .../fit/core/reference/ITImplementationLookup.java | 38 +--------
 .../reference-guide/concepts/notifications.adoc    |  2 +-
 .../workingwithapachesyncope/customization.adoc    |  6 +-
 8 files changed, 164 insertions(+), 71 deletions(-)

diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
index 1e34f6a..57ba53c 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
@@ -22,7 +22,6 @@ import java.io.IOException;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
 import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
-import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.elasticsearch.action.delete.DeleteResponse;
 import org.elasticsearch.action.get.GetResponse;
 import org.elasticsearch.action.index.IndexResponse;
@@ -48,7 +47,8 @@ public class ElasticsearchIndexManager {
 
     @TransactionalEventListener
     public void after(final AnyCreatedUpdatedEvent<Any<?>> event) throws IOException {
-        GetResponse getResponse = client.prepareGet(AuthContextUtils.getDomain().toLowerCase(),
+        GetResponse getResponse = client.prepareGet(
+                elasticsearchUtils.getContextDomainName(event.getAny().getType().getKind()),
                 event.getAny().getType().getKind().name(),
                 event.getAny().getKey()).
                 get();
@@ -56,7 +56,7 @@ public class ElasticsearchIndexManager {
             LOG.debug("About to update index for {}", event.getAny());
 
             UpdateResponse response = client.prepareUpdate(
-                    AuthContextUtils.getDomain().toLowerCase(),
+                    elasticsearchUtils.getContextDomainName(event.getAny().getType().getKind()),
                     event.getAny().getType().getKind().name(),
                     event.getAny().getKey()).
                     setRetryOnConflict(elasticsearchUtils.getRetryOnConflict()).
@@ -67,7 +67,7 @@ public class ElasticsearchIndexManager {
             LOG.debug("About to create index for {}", event.getAny());
 
             IndexResponse response = client.prepareIndex(
-                    AuthContextUtils.getDomain().toLowerCase(),
+                    elasticsearchUtils.getContextDomainName(event.getAny().getType().getKind()),
                     event.getAny().getType().getKind().name(),
                     event.getAny().getKey()).
                     setSource(elasticsearchUtils.builder(event.getAny())).
@@ -82,7 +82,7 @@ public class ElasticsearchIndexManager {
         LOG.debug("About to delete index for {}[{}]", event.getAnyTypeKind(), event.getAnyKey());
 
         DeleteResponse response = client.prepareDelete(
-                AuthContextUtils.getDomain().toLowerCase(),
+                elasticsearchUtils.getContextDomainName(event.getAnyTypeKind()),
                 event.getAnyTypeKind().name(),
                 event.getAnyKey()).
                 get();
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
index a66f1d2..f5b69be 100644
--- 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
@@ -24,6 +24,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
 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;
@@ -33,6 +34,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 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.User;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -182,4 +184,13 @@ public class ElasticsearchUtils {
 
         return builder;
     }
+
+    public String getContextDomainName(final AnyTypeKind kind) {
+        return AuthContextUtils.getDomain().toLowerCase()
+                + (kind.equals(AnyTypeKind.USER)
+                ? "_user"
+                : (kind.equals(AnyTypeKind.GROUP)
+                ? "_group"
+                : "_anyobject"));
+    }
 }
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
index 89f7ac2..02b4a98 100644
--- 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
@@ -52,7 +52,6 @@ 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;
@@ -119,8 +118,7 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
 
         Pair<DisMaxQueryBuilder, Set<String>> filter = adminRealmsFilter(adminRealms);
 
-        return client.prepareSearch(AuthContextUtils.getDomain().toLowerCase()).
-                setTypes(kind.name()).
+        return client.prepareSearch(elasticsearchUtils.getContextDomainName(kind)).
                 setSearchType(SearchType.QUERY_THEN_FETCH).
                 setQuery(SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms)
                         ? getQueryBuilder(cond, kind)
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
index 9a0159f..1a3331f 100644
--- 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
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.provisioning.java.job;
 
+import java.util.concurrent.ExecutionException;
 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;
@@ -66,18 +67,9 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
     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()) {
-                    AcknowledgedResponse acknowledgedResponse = client.admin().indices().
-                            delete(new DeleteIndexRequest(AuthContextUtils.getDomain().toLowerCase())).
-                            get();
-                    LOG.debug("Successfully removed {}: {}",
-                            AuthContextUtils.getDomain().toLowerCase(), acknowledgedResponse);
-                }
+                checkExistsIndexResponse(AnyTypeKind.USER);
+                checkExistsIndexResponse(AnyTypeKind.GROUP);
+                checkExistsIndexResponse(AnyTypeKind.ANY_OBJECT);
 
                 XContentBuilder settings = XContentFactory.jsonBuilder().
                         startObject().
@@ -94,7 +86,35 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
                         endObject().
                         endObject().
                         endObject();
-                XContentBuilder mapping = XContentFactory.jsonBuilder().
+                XContentBuilder mappingUser = XContentFactory.jsonBuilder().
+                        startObject().
+                        startArray("dynamic_templates").
+                        startObject().
+                        startObject("strings").
+                        field("match_mapping_type", "string").
+                        startObject("mapping").
+                        field("type", "keyword").
+                        field("analyzer", "string_lowercase").
+                        endObject().
+                        endObject().
+                        endObject().
+                        endArray().
+                        endObject();
+                XContentBuilder mappingGroup = XContentFactory.jsonBuilder().
+                        startObject().
+                        startArray("dynamic_templates").
+                        startObject().
+                        startObject("strings").
+                        field("match_mapping_type", "string").
+                        startObject("mapping").
+                        field("type", "keyword").
+                        field("analyzer", "string_lowercase").
+                        endObject().
+                        endObject().
+                        endObject().
+                        endArray().
+                        endObject();
+                XContentBuilder mappingAnyobject = XContentFactory.jsonBuilder().
                         startObject().
                         startArray("dynamic_templates").
                         startObject().
@@ -108,21 +128,16 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
                         endObject().
                         endArray().
                         endObject();
-                CreateIndexResponse createIndexResponse = client.admin().indices().
-                        create(new CreateIndexRequest(AuthContextUtils.getDomain().toLowerCase()).
-                                settings(settings).
-                                mapping(AnyTypeKind.USER.name(), mapping).
-                                mapping(AnyTypeKind.GROUP.name(), mapping).
-                                mapping(AnyTypeKind.ANY_OBJECT.name(), mapping)).
-                        get();
-                LOG.debug("Successfully created {}: {}",
-                        AuthContextUtils.getDomain().toLowerCase(), createIndexResponse);
+
+                createIndexResponse(AnyTypeKind.USER, settings, mappingUser);
+                createIndexResponse(AnyTypeKind.GROUP, settings, mappingGroup);
+                createIndexResponse(AnyTypeKind.ANY_OBJECT, settings, mappingAnyobject);
 
                 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(),
+                                elasticsearchUtils.getContextDomainName(AnyTypeKind.USER),
                                 AnyTypeKind.USER.name(),
                                 user.getKey()).
                                 setSource(elasticsearchUtils.builder(user)).
@@ -134,7 +149,7 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
                 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(),
+                                elasticsearchUtils.getContextDomainName(AnyTypeKind.GROUP),
                                 AnyTypeKind.GROUP.name(),
                                 group.getKey()).
                                 setSource(elasticsearchUtils.builder(group)).
@@ -146,7 +161,7 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
                 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(),
+                                elasticsearchUtils.getContextDomainName(AnyTypeKind.ANY_OBJECT),
                                 AnyTypeKind.ANY_OBJECT.name(),
                                 anyObject.getKey()).
                                 setSource(elasticsearchUtils.builder(anyObject)).
@@ -165,6 +180,34 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
         return "SUCCESS";
     }
 
+    private void checkExistsIndexResponse(final AnyTypeKind kind) throws InterruptedException, ExecutionException {
+        LOG.debug("Start rebuild index {}",
+                elasticsearchUtils.getContextDomainName(kind));
+        IndicesExistsResponse existsIndexResponse = client.admin().indices().
+                exists(new IndicesExistsRequest(elasticsearchUtils.getContextDomainName(kind))).
+                get();
+        if (existsIndexResponse.isExists()) {
+            AcknowledgedResponse acknowledgedResponse = client.admin().indices().
+                    delete(new DeleteIndexRequest(elasticsearchUtils.getContextDomainName(kind))).
+                    get();
+            LOG.debug("Successfully removed {}: {}",
+                    elasticsearchUtils.getContextDomainName(kind), acknowledgedResponse);
+        }
+    }
+
+    private void createIndexResponse(final AnyTypeKind kind,
+            final XContentBuilder settings,
+            final XContentBuilder mapping) throws InterruptedException, ExecutionException {
+
+        CreateIndexResponse createIndexResponseUser = client.admin().indices().
+                create(new CreateIndexRequest(elasticsearchUtils.getContextDomainName(kind)).
+                        settings(settings).
+                        mapping(kind.name(), mapping)).
+                get();
+        LOG.debug("Successfully created {} for {}: {}",
+                elasticsearchUtils.getContextDomainName(kind), kind.name(), createIndexResponseUser);
+    }
+
     @Override
     protected boolean hasToBeRegistered(final TaskExec execution) {
         return true;
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/core/logic/init/ElasticsearchInit.java b/fit/core-reference/src/main/java/org/apache/syncope/core/logic/init/ElasticsearchInit.java
new file mode 100644
index 0000000..60d1893
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/core/logic/init/ElasticsearchInit.java
@@ -0,0 +1,71 @@
+/*
+ * 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.logic.init;
+
+import org.apache.syncope.common.lib.to.SchedTaskTO;
+import org.apache.syncope.common.lib.types.ImplementationEngine;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.common.lib.types.TaskType;
+import org.apache.syncope.core.logic.TaskLogic;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class ElasticsearchInit {
+
+    @Autowired
+    private ImplementationDAO implementationDAO;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private TaskLogic taskLogic;
+
+    private static final String ES_REINDEX = "org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex";
+
+    @Transactional
+    public void init() {
+        Implementation reindex = implementationDAO.find(ImplementationType.TASKJOB_DELEGATE).
+                stream().
+                filter(impl -> impl.getEngine() == ImplementationEngine.JAVA
+                && ES_REINDEX.equals(impl.getBody())).
+                findAny().orElse(null);
+        if (reindex == null) {
+            reindex = entityFactory.newEntity(Implementation.class);
+            reindex.setKey(ES_REINDEX);
+            reindex.setEngine(ImplementationEngine.JAVA);
+            reindex.setType(ImplementationType.TASKJOB_DELEGATE);
+            reindex.setBody(ES_REINDEX);
+            reindex = implementationDAO.save(reindex);
+        }
+
+        SchedTaskTO task = new SchedTaskTO();
+        task.setJobDelegate(reindex.getKey());
+        task.setName("Elasticsearch Reindex");
+        task = taskLogic.createSchedTask(TaskType.SCHEDULED, task);
+
+        taskLogic.execute(task.getKey(), null, false);
+    }
+
+}
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 b3b03f3..c8c2a5e 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
@@ -40,11 +40,8 @@ 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.common.lib.types.ImplementationEngine;
 import org.apache.syncope.common.lib.types.ImplementationType;
-import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.core.logic.TaskLogic;
+import org.apache.syncope.core.logic.init.ElasticsearchInit;
 import org.apache.syncope.core.provisioning.java.job.report.AuditReportlet;
 import org.apache.syncope.core.provisioning.java.job.report.GroupReportlet;
 import org.apache.syncope.core.provisioning.java.job.report.ReconciliationReportlet;
@@ -54,13 +51,10 @@ 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.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
-import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.jpa.attrvalue.validation.AlwaysTrueValidator;
 import org.apache.syncope.core.persistence.jpa.attrvalue.validation.BasicValidator;
 import org.apache.syncope.core.persistence.jpa.attrvalue.validation.BinaryValidator;
@@ -88,8 +82,6 @@ import org.springframework.beans.factory.annotation.Autowired;
  */
 public class ITImplementationLookup implements ImplementationLookup {
 
-    private static final String ES_REINDEX = "org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex";
-
     private static final Set<Class<?>> JWTSSOPROVIDER_CLASSES = new HashSet<>(
             Arrays.asList(SyncopeJWTSSOProvider.class, CustomJWTSSOProvider.class));
 
@@ -243,16 +235,10 @@ public class ITImplementationLookup implements ImplementationLookup {
     private AnySearchDAO anySearchDAO;
 
     @Autowired
-    private ImplementationDAO implementationDAO;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
     private DomainsHolder domainsHolder;
 
     @Autowired
-    private TaskLogic taskLogic;
+    private ElasticsearchInit elasticsearchInit;
 
     @Override
     public Integer getPriority() {
@@ -265,25 +251,7 @@ public class ITImplementationLookup implements ImplementationLookup {
         if (AopUtils.getTargetClass(anySearchDAO).getName().contains("Elasticsearch")) {
             for (Map.Entry<String, DataSource> entry : domainsHolder.getDomains().entrySet()) {
                 AuthContextUtils.execWithAuthContext(entry.getKey(), () -> {
-                    Implementation reindex = implementationDAO.find(ImplementationType.TASKJOB_DELEGATE).
-                            stream().
-                            filter(impl -> impl.getEngine() == ImplementationEngine.JAVA
-                            && ES_REINDEX.equals(impl.getBody())).
-                            findAny().orElse(null);
-                    if (reindex == null) {
-                        reindex = entityFactory.newEntity(Implementation.class);
-                        reindex.setEngine(ImplementationEngine.JAVA);
-                        reindex.setType(ImplementationType.TASKJOB_DELEGATE);
-                        reindex.setBody(ES_REINDEX);
-                        reindex = implementationDAO.save(reindex);
-                    }
-
-                    SchedTaskTO task = new SchedTaskTO();
-                    task.setJobDelegate(reindex.getKey());
-                    task.setName("Elasticsearch Reindex");
-                    task = taskLogic.createSchedTask(TaskType.SCHEDULED, task);
-
-                    taskLogic.execute(task.getKey(), null, false);
+                    elasticsearchInit.init();
 
                     return null;
                 });
diff --git a/src/main/asciidoc/reference-guide/concepts/notifications.adoc b/src/main/asciidoc/reference-guide/concepts/notifications.adoc
index 6d2d06f..51a5b70 100644
--- a/src/main/asciidoc/reference-guide/concepts/notifications.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/notifications.adoc
@@ -65,7 +65,7 @@ An event is identified by the following five coordinates:
 ** `PUSH`
 ** `CUSTOM`
 . category - the possible values depend on the selected type: for `LOGIC` the <<logic>> components available,
-for `TASK` the various <<tasks-custom, Custom Task>> configured, for `PROPAGATION`, `PULL` and `PUSH` the defined Any Types
+for `TASK` the various <<tasks-custom, Custom Tasks>> configured, for `PROPAGATION`, `PULL` and `PUSH` the defined Any Types
 . subcategory - completes category with external resource name, when selecting `PROPAGATION`, `PULL` or `PUSH`
 . event type - the final identification of the event; depends on the other coordinates
 . success or failure - whether the current event shall be considered in case of success or failure
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
index ec40b4f..11104fd 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
@@ -489,8 +489,10 @@ classpath*:/workflow*Context.xml
 
 It is also required to initialize the Elasticsearch indexes.
 
-Create a new <<tasks-custom, Custom task>>, specify the 
-`org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex` as job delegate and execute it.
+Add a new Java <<implementations,implementation>> for `TASKJOB_DELEGATE` and use 
+`org.apache.syncope.core.provisioning.java.job.ElasticsearchReindex` as class.
+
+Then, create a new <<tasks-custom, Custom task>>, select the implementation just created as job delegate and execute it.
 
 [discrete]
 ===== Enable the <<SCIM>> extension