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:06:22 UTC

[syncope] branch master updated: [SYNCOPE-1399] Added fix for Elasticsearch v6.x

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 85fe179  [SYNCOPE-1399] Added fix for Elasticsearch v6.x
85fe179 is described below

commit 85fe179ec541685e28b83cea55cec2b9341a50c3
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    |  7 ++
 8 files changed, 167 insertions(+), 69 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 d1c69ae..71ac025 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 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 986de12..1c4ec41 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
@@ -487,6 +487,13 @@ classpath*:/provisioning*Context.xml
 classpath*:/workflow*Context.xml
 ....
 
+It is also required to initialize the Elasticsearch indexes.
+
+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