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