This is an automated email from the ASF dual-hosted git repository. ilgrosso 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 4880e0d Upgrading Elasticsearch (#298) 4880e0d is described below commit 4880e0dd52ab243c1a56e41bb2d7a6ff2dd6e37d Author: Francesco Chicchiriccò <ilgro...@users.noreply.github.com> AuthorDate: Tue Dec 21 10:57:18 2021 +0100 Upgrading Elasticsearch (#298) --- ext/elasticsearch/client-elasticsearch/pom.xml | 18 +- .../client/ElasticsearchClientContext.java | 5 +- .../client/ElasticsearchClientFactoryBean.java | 32 +- .../client/ElasticsearchIndexManager.java | 177 +++++----- .../elasticsearch/client/ElasticsearchUtils.java | 118 +++---- ext/elasticsearch/persistence-jpa/pom.xml | 5 + .../jpa/ElasticsearchPersistenceContext.java | 4 +- .../jpa/dao/ElasticsearchAnySearchDAO.java | 377 ++++++++++++--------- .../jpa/dao/ElasticsearchAnySearchDAOTest.java | 58 ++-- ext/elasticsearch/provisioning-java/pom.xml | 2 +- .../java/job/ElasticsearchReindex.java | 52 +-- .../syncope/fit/core/NotificationTaskITCase.java | 6 +- pom.xml | 30 +- 13 files changed, 459 insertions(+), 425 deletions(-) diff --git a/ext/elasticsearch/client-elasticsearch/pom.xml b/ext/elasticsearch/client-elasticsearch/pom.xml index d7bd416..10bf6c8 100644 --- a/ext/elasticsearch/client-elasticsearch/pom.xml +++ b/ext/elasticsearch/client-elasticsearch/pom.xml @@ -45,25 +45,21 @@ under the License. </dependency> <dependency> - <groupId>org.elasticsearch.client</groupId> - <artifactId>elasticsearch-rest-high-level-client</artifactId> + <groupId>co.elastic.clients</groupId> + <artifactId>elasticsearch-java</artifactId> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> </dependency> <dependency> - <groupId>org.elasticsearch</groupId> - <artifactId>elasticsearch</artifactId> - </dependency> - - <dependency> - <groupId>com.fasterxml.jackson.dataformat</groupId> - <artifactId>jackson-dataformat-smile</artifactId> + <groupId>jakarta.json</groupId> + <artifactId>jakarta.json-api</artifactId> </dependency> + <dependency> - <groupId>com.fasterxml.jackson.dataformat</groupId> - <artifactId>jackson-dataformat-cbor</artifactId> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> </dependency> </dependencies> diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java index 7e37d50..dbacb5e 100644 --- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java +++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java @@ -18,13 +18,12 @@ */ package org.apache.syncope.ext.elasticsearch.client; +import co.elastic.clients.elasticsearch.ElasticsearchClient; import java.util.List; - import org.apache.http.HttpHost; 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.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -61,7 +60,7 @@ public class ElasticsearchClientContext { @Bean @Autowired public ElasticsearchIndexManager elasticsearchIndexManager( - final RestHighLevelClient client, + final ElasticsearchClient client, final ElasticsearchUtils elasticsearchUtils) { return new ElasticsearchIndexManager(client, elasticsearchUtils); diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java index 39246a9..1a6aae4 100644 --- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java +++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java @@ -18,6 +18,9 @@ */ package org.apache.syncope.ext.elasticsearch.client; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.rest_client.RestClientTransport; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.List; @@ -31,14 +34,13 @@ import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.message.BasicHeader; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; -import org.elasticsearch.client.RestHighLevelClient; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; /** - * Spring {@link FactoryBean} for getting the Elasticsearch's {@link RestHighLevelClient} singleton instance. + * Spring {@link FactoryBean} for getting the {@link ElasticsearchClient} singleton instance. */ -public class ElasticsearchClientFactoryBean implements FactoryBean<RestHighLevelClient>, DisposableBean { +public class ElasticsearchClientFactoryBean implements FactoryBean<ElasticsearchClient>, DisposableBean { private final List<HttpHost> hosts; @@ -52,7 +54,9 @@ public class ElasticsearchClientFactoryBean implements FactoryBean<RestHighLevel private String apiKeySecret; - private RestHighLevelClient client; + private RestClient restClient; + + private ElasticsearchClient client; public ElasticsearchClientFactoryBean(final List<HttpHost> hosts) { this.hosts = hosts; @@ -91,25 +95,27 @@ public class ElasticsearchClientFactoryBean implements FactoryBean<RestHighLevel } @Override - public RestHighLevelClient getObject() throws Exception { + public ElasticsearchClient getObject() throws Exception { synchronized (this) { if (client == null) { - RestClientBuilder restClient = RestClient.builder(hosts.toArray(new HttpHost[0])); + RestClientBuilder builder = RestClient.builder(hosts.toArray(HttpHost[]::new)); if (username != null && password != null) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(username, password)); - restClient.setHttpClientConfigCallback(b -> b.setDefaultCredentialsProvider(credentialsProvider)); + builder.setHttpClientConfigCallback(b -> b.setDefaultCredentialsProvider(credentialsProvider)); } else if (serviceToken != null) { - restClient.setDefaultHeaders( + builder.setDefaultHeaders( new Header[] { new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + serviceToken) }); } else if (apiKeyId != null && apiKeySecret != null) { String apiKeyAuth = Base64.getEncoder().encodeToString( (apiKeyId + ":" + apiKeySecret).getBytes(StandardCharsets.UTF_8)); - restClient.setDefaultHeaders( + builder.setDefaultHeaders( new Header[] { new BasicHeader(HttpHeaders.AUTHORIZATION, "ApiKey " + apiKeyAuth) }); } - client = new RestHighLevelClient(restClient); + + restClient = builder.build(); + client = new ElasticsearchClient(new RestClientTransport(restClient, new JacksonJsonpMapper())); } } return client; @@ -117,13 +123,13 @@ public class ElasticsearchClientFactoryBean implements FactoryBean<RestHighLevel @Override public Class<?> getObjectType() { - return RestHighLevelClient.class; + return ElasticsearchClient.class; } @Override public void destroy() throws Exception { - if (client != null) { - client.close(); + if (restClient != null) { + restClient.close(); } } } 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 b313dca..6aaef95 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 @@ -18,30 +18,32 @@ */ package org.apache.syncope.ext.elasticsearch.client; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.ElasticsearchException; +import co.elastic.clients.elasticsearch._types.analysis.CustomNormalizer; +import co.elastic.clients.elasticsearch._types.analysis.Normalizer; +import co.elastic.clients.elasticsearch._types.mapping.DynamicTemplate; +import co.elastic.clients.elasticsearch._types.mapping.KeywordProperty; +import co.elastic.clients.elasticsearch._types.mapping.Property; +import co.elastic.clients.elasticsearch._types.mapping.TypeMapping; +import co.elastic.clients.elasticsearch.core.DeleteRequest; +import co.elastic.clients.elasticsearch.core.DeleteResponse; +import co.elastic.clients.elasticsearch.core.IndexRequest; +import co.elastic.clients.elasticsearch.core.IndexResponse; +import co.elastic.clients.elasticsearch.indices.CreateIndexRequest; +import co.elastic.clients.elasticsearch.indices.CreateIndexResponse; +import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest; +import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse; +import co.elastic.clients.elasticsearch.indices.IndexSettings; +import co.elastic.clients.elasticsearch.indices.IndexSettingsAnalysis; import java.io.IOException; +import java.util.List; +import java.util.Map; import org.apache.syncope.common.lib.types.AnyTypeKind; 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.ElasticsearchStatusException; -import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; -import org.elasticsearch.action.delete.DeleteRequest; -import org.elasticsearch.action.delete.DeleteResponse; -import org.elasticsearch.action.get.GetRequest; -import org.elasticsearch.action.get.GetResponse; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.action.update.UpdateRequest; -import org.elasticsearch.action.update.UpdateResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.client.indices.CreateIndexRequest; -import org.elasticsearch.client.indices.CreateIndexResponse; -import org.elasticsearch.client.indices.GetIndexRequest; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.event.TransactionalEventListener; @@ -53,12 +55,12 @@ public class ElasticsearchIndexManager { private static final Logger LOG = LoggerFactory.getLogger(ElasticsearchIndexManager.class); - protected final RestHighLevelClient client; + protected final ElasticsearchClient client; protected final ElasticsearchUtils elasticsearchUtils; public ElasticsearchIndexManager( - final RestHighLevelClient client, + final ElasticsearchClient client, final ElasticsearchUtils elasticsearchUtils) { this.client = client; @@ -67,127 +69,104 @@ public class ElasticsearchIndexManager { public boolean existsIndex(final String domain, final AnyTypeKind kind) throws IOException { return client.indices().exists( - new GetIndexRequest(ElasticsearchUtils.getContextDomainName(domain, kind)), RequestOptions.DEFAULT); + new co.elastic.clients.elasticsearch.indices.ExistsRequest.Builder(). + index(ElasticsearchUtils.getContextDomainName(domain, kind)).build()). + value(); } - public XContentBuilder defaultSettings() throws IOException { - return XContentFactory.jsonBuilder(). - startObject(). - startObject("analysis"). - startObject("normalizer"). - startObject("string_lowercase"). - field("type", "custom"). - field("char_filter", new Object[0]). - field("filter"). - startArray(). - value("lowercase"). - endArray(). - endObject(). - endObject(). - endObject(). - startObject("index"). - field("number_of_shards", elasticsearchUtils.getNumberOfShards()). - field("number_of_replicas", elasticsearchUtils.getNumberOfReplicas()). - endObject(). - endObject(); + public IndexSettings defaultSettings() throws IOException { + return new IndexSettings.Builder(). + analysis(new IndexSettingsAnalysis.Builder(). + normalizer("string_lowercase", new Normalizer.Builder(). + custom(new CustomNormalizer.Builder(). + charFilter(List.of()). + filter("lowercase"). + build()). + build()). + build()). + numberOfShards(elasticsearchUtils.getNumberOfShards()). + numberOfReplicas(elasticsearchUtils.getNumberOfReplicas()). + build(); } - public XContentBuilder defaultMapping() throws IOException { - return XContentFactory.jsonBuilder(). - startObject(). - startArray("dynamic_templates"). - startObject(). - startObject("strings"). - field("match_mapping_type", "string"). - startObject("mapping"). - field("type", "keyword"). - field("normalizer", "string_lowercase"). - endObject(). - endObject(). - endObject(). - endArray(). - endObject(); + public TypeMapping defaultMapping() throws IOException { + return new TypeMapping.Builder(). + dynamicTemplates(List.of(Map.of( + "strings", + new DynamicTemplate.Builder(). + matchMappingType("string"). + mapping(new Property.Builder(). + keyword(new KeywordProperty.Builder().normalizer("string_lowercase").build()). + build()). + build()))). + build(); } protected CreateIndexResponse doCreateIndex( final String domain, final AnyTypeKind kind, - final XContentBuilder settings, - final XContentBuilder mapping) throws IOException { + final IndexSettings settings, + final TypeMapping mappings) throws IOException { return client.indices().create( - new CreateIndexRequest(ElasticsearchUtils.getContextDomainName(domain, kind)). + new CreateIndexRequest.Builder(). + index(ElasticsearchUtils.getContextDomainName(domain, kind)). settings(settings). - mapping(mapping), RequestOptions.DEFAULT); + mappings(mappings). + build()); } public void createIndex( final String domain, final AnyTypeKind kind, - final XContentBuilder settings, - final XContentBuilder mapping) + final IndexSettings settings, + final TypeMapping mappings) throws IOException { try { - CreateIndexResponse response = doCreateIndex(domain, kind, settings, mapping); + CreateIndexResponse response = doCreateIndex(domain, kind, settings, mappings); LOG.debug("Successfully created {} for {}: {}", ElasticsearchUtils.getContextDomainName(domain, kind), kind.name(), response); - } catch (ElasticsearchStatusException e) { + } catch (ElasticsearchException e) { LOG.debug("Could not create index {} because it already exists", ElasticsearchUtils.getContextDomainName(domain, kind), e); removeIndex(domain, kind); - doCreateIndex(domain, kind, settings, mapping); + doCreateIndex(domain, kind, settings, mappings); } } public void removeIndex(final String domain, final AnyTypeKind kind) throws IOException { - AcknowledgedResponse acknowledgedResponse = client.indices().delete( - new DeleteIndexRequest(ElasticsearchUtils.getContextDomainName(domain, kind)), RequestOptions.DEFAULT); + DeleteIndexResponse response = client.indices().delete( + new DeleteIndexRequest.Builder().index(ElasticsearchUtils.getContextDomainName(domain, kind)).build()); LOG.debug("Successfully removed {}: {}", - ElasticsearchUtils.getContextDomainName(domain, kind), acknowledgedResponse); + ElasticsearchUtils.getContextDomainName(domain, kind), response); } @TransactionalEventListener public void after(final AnyCreatedUpdatedEvent<Any<?>> event) throws IOException { - GetRequest getRequest = new GetRequest( - ElasticsearchUtils.getContextDomainName( - AuthContextUtils.getDomain(), event.getAny().getType().getKind()), - event.getAny().getKey()); - GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT); - if (getResponse.isExists()) { - LOG.debug("About to update index for {}", event.getAny()); - - UpdateRequest request = new UpdateRequest( - ElasticsearchUtils.getContextDomainName( - AuthContextUtils.getDomain(), event.getAny().getType().getKind()), - event.getAny().getKey()). - retryOnConflict(elasticsearchUtils.getRetryOnConflict()). - doc(elasticsearchUtils.builder(event.getAny(), event.getDomain())); - UpdateResponse response = client.update(request, RequestOptions.DEFAULT); - LOG.debug("Index successfully updated for {}: {}", event.getAny(), response); - } else { - LOG.debug("About to create index for {}", event.getAny()); - - IndexRequest request = new IndexRequest( - ElasticsearchUtils.getContextDomainName( - AuthContextUtils.getDomain(), event.getAny().getType().getKind())). - id(event.getAny().getKey()). - source(elasticsearchUtils.builder(event.getAny(), event.getDomain())); - IndexResponse response = client.index(request, RequestOptions.DEFAULT); - LOG.debug("Index successfully created for {}: {}", event.getAny(), response); - } + String index = ElasticsearchUtils.getContextDomainName( + AuthContextUtils.getDomain(), event.getAny().getType().getKind()); + + IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>(). + index(index). + id(event.getAny().getKey()). + document(elasticsearchUtils.document(event.getAny(), event.getDomain())). + build(); + IndexResponse response = client.index(request); + LOG.debug("Index successfully created or updated for {}: {}", event.getAny(), response); } @TransactionalEventListener public void after(final AnyDeletedEvent event) throws IOException { LOG.debug("About to delete index for {}[{}]", event.getAnyTypeKind(), event.getAnyKey()); - DeleteRequest request = new DeleteRequest( - ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), event.getAnyTypeKind()), - event.getAnyKey()); - DeleteResponse response = client.delete(request, RequestOptions.DEFAULT); + DeleteRequest request = new DeleteRequest.Builder().index( + ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), event.getAnyTypeKind())). + id(event.getAnyKey()). + build(); + DeleteResponse response = client.delete(request); LOG.debug("Index successfully deleted for {}[{}]: {}", event.getAnyTypeKind(), event.getAnyKey(), response); } 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 8ea8a24..54d2385 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 @@ -21,8 +21,10 @@ package org.apache.syncope.ext.elasticsearch.client; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.apache.syncope.common.lib.types.AnyTypeKind; @@ -37,8 +39,6 @@ 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.transaction.annotation.Transactional; /** @@ -60,9 +60,9 @@ public class ElasticsearchUtils { protected int retryOnConflict = 5; - protected int numberOfShards = 1; + protected String numberOfShards = "1"; - protected int numberOfReplicas = 1; + protected String numberOfReplicas = "1"; public ElasticsearchUtils(final UserDAO userDAO, final GroupDAO groupDAO, final AnyObjectDAO anyObjectDAO) { this.userDAO = userDAO; @@ -86,32 +86,32 @@ public class ElasticsearchUtils { return retryOnConflict; } - public int getNumberOfShards() { + public String getNumberOfShards() { return numberOfShards; } public void setNumberOfShards(final int numberOfShards) { - this.numberOfShards = numberOfShards; + this.numberOfShards = String.valueOf(numberOfShards); } - public int getNumberOfReplicas() { + public String getNumberOfReplicas() { return numberOfReplicas; } public void setNumberOfReplicas(final int numberOfReplicas) { - this.numberOfReplicas = numberOfReplicas; + this.numberOfReplicas = String.valueOf(numberOfReplicas); } /** - * Returns the builder specialized with content from the provided any. + * Returns the document specialized with content from the provided any. * * @param any user, group or any object to index * @param domain tenant information - * @return builder specialized with content from the provided any + * @return document specialized with content from the provided any * @throws IOException in case of errors */ @Transactional - public XContentBuilder builder(final Any<?> any, final String domain) throws IOException { + public Map<String, Object> document(final Any<?> any, final String domain) throws IOException { Set<String> resources = new HashSet<>(); List<String> dynRealms = new ArrayList<>(); AuthContextUtils.callAsAdmin(domain, () -> { @@ -128,28 +128,27 @@ public class ElasticsearchUtils { return null; }); - XContentBuilder builder = XContentFactory.jsonBuilder(). - startObject(). - field("id", any.getKey()). - field("realm", any.getRealm().getFullPath()). - field("anyType", any.getType().getKey()). - field("creationDate", any.getCreationDate()). - field("creationContext", any.getCreationContext()). - field("creator", any.getCreator()). - field("lastChangeDate", any.getLastChangeDate()). - field("lastModifier", any.getLastModifier()). - field("lastChangeContext", any.getLastChangeContext()). - field("status", any.getStatus()). - field("resources", resources). - field("dynRealms", dynRealms); + Map<String, Object> builder = new HashMap<>(); + builder.put("id", any.getKey()); + builder.put("realm", any.getRealm().getFullPath()); + builder.put("anyType", any.getType().getKey()); + builder.put("creationDate", any.getCreationDate()); + builder.put("creationContext", any.getCreationContext()); + builder.put("creator", any.getCreator()); + builder.put("lastChangeDate", any.getLastChangeDate()); + builder.put("lastModifier", any.getLastModifier()); + builder.put("lastChangeContext", any.getLastChangeContext()); + builder.put("status", any.getStatus()); + builder.put("resources", resources); + builder.put("dynRealms", dynRealms); if (any instanceof AnyObject) { AnyObject anyObject = ((AnyObject) any); - builder = builder.field("name", anyObject.getName()); + builder.put("name", anyObject.getName()); Collection<String> memberships = AuthContextUtils.callAsAdmin( domain, () -> anyObjectDAO.findAllGroupKeys(anyObject)); - builder = builder.field("memberships", memberships); + builder.put("memberships", memberships); List<String> relationships = new ArrayList<>(); List<String> relationshipTypes = new ArrayList<>(); @@ -160,18 +159,18 @@ public class ElasticsearchUtils { }); return null; }); - builder = builder.field("relationships", relationships); - builder = builder.field("relationshipTypes", relationshipTypes); + builder.put("relationships", relationships); + builder.put("relationshipTypes", relationshipTypes); - builder = customizeBuilder(builder, anyObject, domain); + ElasticsearchUtils.this.customizeDocument(builder, anyObject, domain); } else if (any instanceof Group) { Group group = ((Group) any); - builder = builder.field("name", group.getName()); + builder.put("name", group.getName()); if (group.getUserOwner() != null) { - builder = builder.field("userOwner", group.getUserOwner().getKey()); + builder.put("userOwner", group.getUserOwner().getKey()); } if (group.getGroupOwner() != null) { - builder = builder.field("groupOwner", group.getGroupOwner().getKey()); + builder.put("groupOwner", group.getGroupOwner().getKey()); } Set<String> members = new HashSet<>(); @@ -184,20 +183,19 @@ public class ElasticsearchUtils { members.addAll(groupDAO.findADynMembers(group)); return null; }); - builder = builder.field("members", members); + builder.put("members", members); - builder = customizeBuilder(builder, group, domain); + ElasticsearchUtils.this.customizeDocument(builder, group, domain); } else if (any instanceof User) { User user = ((User) any); - builder = builder. - field("username", user.getUsername()). - field("token", user.getToken()). - field("tokenExpireTime", user.getTokenExpireTime()). - field("changePwdDate", user.getChangePwdDate()). - field("failedLogins", user.getFailedLogins()). - field("lastLoginDate", user.getLastLoginDate()). - field("suspended", user.isSuspended()). - field("mustChangePassword", user.isMustChangePassword()); + builder.put("username", user.getUsername()); + builder.put("token", user.getToken()); + builder.put("tokenExpireTime", user.getTokenExpireTime()); + builder.put("changePwdDate", user.getChangePwdDate()); + builder.put("failedLogins", user.getFailedLogins()); + builder.put("lastLoginDate", user.getLastLoginDate()); + builder.put("suspended", user.isSuspended()); + builder.put("mustChangePassword", user.isMustChangePassword()); List<String> roles = new ArrayList<>(); Set<String> privileges = new HashSet<>(); @@ -208,12 +206,12 @@ public class ElasticsearchUtils { }); return null; }); - builder = builder.field("roles", roles); - builder = builder.field("privileges", privileges); + builder.put("roles", roles); + builder.put("privileges", privileges); Collection<String> memberships = AuthContextUtils.callAsAdmin( domain, () -> userDAO.findAllGroupKeys(user)); - builder = builder.field("memberships", memberships); + builder.put("memberships", memberships); List<String> relationships = new ArrayList<>(); Set<String> relationshipTypes = new HashSet<>(); @@ -221,10 +219,10 @@ public class ElasticsearchUtils { relationships.add(relationship.getRightEnd().getKey()); relationshipTypes.add(relationship.getType().getKey()); }); - builder = builder.field("relationships", relationships); - builder = builder.field("relationshipTypes", relationshipTypes); + builder.put("relationships", relationships); + builder.put("relationshipTypes", relationshipTypes); - builder = customizeBuilder(builder, user, domain); + customizeDocument(builder, user, domain); } for (PlainAttr<?> plainAttr : any.getPlainAttrs()) { @@ -235,29 +233,23 @@ public class ElasticsearchUtils { values.add(plainAttr.getUniqueValue().getValue()); } - builder = builder.field(plainAttr.getSchema().getKey(), values.size() == 1 ? values.get(0) : values); + builder.put(plainAttr.getSchema().getKey(), values.size() == 1 ? values.get(0) : values); } - return builder.endObject(); + return builder; } - protected XContentBuilder customizeBuilder( - final XContentBuilder builder, final AnyObject anyObject, final String domain) + protected void customizeDocument( + final Map<String, Object> builder, final AnyObject anyObject, final String domain) throws IOException { - - return builder; } - protected XContentBuilder customizeBuilder( - final XContentBuilder builder, final Group group, final String domain) + protected void customizeDocument( + final Map<String, Object> builder, final Group group, final String domain) throws IOException { - - return builder; } - protected XContentBuilder customizeBuilder(final XContentBuilder builder, final User user, final String domain) + protected void customizeDocument(final Map<String, Object> builder, final User user, final String domain) throws IOException { - - return builder; } } diff --git a/ext/elasticsearch/persistence-jpa/pom.xml b/ext/elasticsearch/persistence-jpa/pom.xml index b9dcfcd..3c935e8 100644 --- a/ext/elasticsearch/persistence-jpa/pom.xml +++ b/ext/elasticsearch/persistence-jpa/pom.xml @@ -66,6 +66,11 @@ under the License. <artifactId>mockito-junit-jupiter</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java index a869507..ccc8421 100644 --- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java +++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java @@ -30,7 +30,7 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils; -import org.elasticsearch.client.RestHighLevelClient; +import co.elastic.clients.elasticsearch.ElasticsearchClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -59,7 +59,7 @@ public class ElasticsearchPersistenceContext { final PlainSchemaDAO schemaDAO, final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, - final RestHighLevelClient client, + final ElasticsearchClient client, final @Lazy ElasticsearchUtils elasticsearchUtils) { return new ElasticsearchAnySearchDAO( 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 c2aaa9b..bd014ba 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 @@ -18,15 +18,29 @@ */ package org.apache.syncope.core.persistence.jpa.dao; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.FieldSort; +import co.elastic.clients.elasticsearch._types.FieldValue; +import co.elastic.clients.elasticsearch._types.SearchType; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.MatchNoneQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; +import co.elastic.clients.elasticsearch.core.CountRequest; +import co.elastic.clients.elasticsearch.core.SearchRequest; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.json.JsonData; import java.io.IOException; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; @@ -67,30 +81,18 @@ 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.SearchRequest; -import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.client.core.CountRequest; -import org.elasticsearch.index.query.DisMaxQueryBuilder; -import org.elasticsearch.index.query.MatchAllQueryBuilder; -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.elasticsearch.search.builder.SearchSourceBuilder; -import org.elasticsearch.search.sort.FieldSortBuilder; -import org.elasticsearch.search.sort.SortBuilder; -import org.elasticsearch.search.sort.SortOrder; +import org.springframework.util.CollectionUtils; /** * Search engine implementation for users, groups and any objects, based on Elasticsearch. */ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { - protected static final QueryBuilder MATCH_NONE_QUERY_BUILDER = new MatchNoneQueryBuilder(); + protected static final Query MATCH_NONE_QUERY = + new Query.Builder().matchNone(new MatchNoneQuery.Builder().build()).build(); - protected static final QueryBuilder MATCH_ALL_QUERY_BUILDER = new MatchAllQueryBuilder(); + protected static final Query MATCH_ALL_QUERY = + new Query.Builder().matchAll(new MatchAllQuery.Builder().build()).build(); protected static final char[] ELASTICSEARCH_REGEX_CHARS = new char[] { '.', '?', '+', '*', '|', '{', '}', '[', ']', '(', ')', '"', '\\', '&' }; @@ -107,7 +109,7 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { return output.toString(); } - protected final RestHighLevelClient client; + protected final ElasticsearchClient client; protected final ElasticsearchUtils elasticsearchUtils; @@ -120,7 +122,7 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { final PlainSchemaDAO schemaDAO, final EntityFactory entityFactory, final AnyUtilsFactory anyUtilsFactory, - final RestHighLevelClient client, + final ElasticsearchClient client, final ElasticsearchUtils elasticsearchUtils) { super(realmDAO, dynRealmDAO, userDAO, groupDAO, anyObjectDAO, schemaDAO, entityFactory, anyUtilsFactory); @@ -128,13 +130,12 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { this.elasticsearchUtils = elasticsearchUtils; } - protected Triple<Optional<QueryBuilder>, Set<String>, Set<String>> getAdminRealmsFilter( + protected Triple<Optional<Query>, Set<String>, Set<String>> getAdminRealmsFilter( final AnyTypeKind kind, final Set<String> adminRealms) { - DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery(); - Set<String> dynRealmKeys = new HashSet<>(); Set<String> groupOwners = new HashSet<>(); + List<Query> queries = new ArrayList<>(); adminRealms.forEach(realmPath -> { Optional<Pair<String, String>> goRealm = RealmUtils.parseGroupOwnerRealm(realmPath); @@ -147,8 +148,10 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { noRealm.getElements().add("Invalid realm specified: " + realmPath); throw noRealm; } else { - realmDAO.findDescendants(realm).forEach( - descendant -> builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath()))); + realmDAO.findDescendants(realm).forEach(descendant -> queries.add( + new Query.Builder().term(QueryBuilders.term(). + field("realm").value(FieldValue.of(descendant.getFullPath())).build()). + build())); } } else { DynRealm dynRealm = dynRealmDAO.find(realmPath); @@ -156,59 +159,66 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { LOG.warn("Ignoring invalid dynamic realm {}", realmPath); } else { dynRealmKeys.add(dynRealm.getKey()); - builder.add(QueryBuilders.termQuery("dynRealm", dynRealm.getKey())); + queries.add(new Query.Builder().term(QueryBuilders.term(). + field("dynRealm").value(FieldValue.of(dynRealm.getKey())).build()). + build()); } } }); return Triple.of( - dynRealmKeys.isEmpty() && groupOwners.isEmpty() ? Optional.of(builder) : Optional.empty(), + dynRealmKeys.isEmpty() && groupOwners.isEmpty() + ? Optional.of(new Query.Builder().disMax(QueryBuilders.disMax().queries(queries).build()).build()) + : Optional.empty(), dynRealmKeys, groupOwners); } - protected QueryBuilder getQueryBuilder( + protected Query getQuery( final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) { - Triple<Optional<QueryBuilder>, Set<String>, Set<String>> filter = getAdminRealmsFilter(kind, adminRealms); - QueryBuilder queryBuilder; + Triple<Optional<Query>, Set<String>, Set<String>> filter = getAdminRealmsFilter(kind, adminRealms); + Query query; if (SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms)) { - queryBuilder = getQueryBuilder(cond, kind); + query = getQuery(cond, kind); } else { - queryBuilder = getQueryBuilder(buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), kind); + query = getQuery(buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), kind); if (filter.getLeft().isPresent()) { - queryBuilder = QueryBuilders.boolQuery(). - must(filter.getLeft().get()). - must(queryBuilder); + query = new Query.Builder().bool( + QueryBuilders.bool(). + must(filter.getLeft().get()). + must(query).build()). + build(); } } - return queryBuilder; + return query; } @Override protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) { - CountRequest request = new CountRequest( - ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), kind)). - query(getQueryBuilder(adminRealms, cond, kind)); + CountRequest request = new CountRequest.Builder(). + index(ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), kind)). + query(getQuery(adminRealms, cond, kind)). + build(); try { - return (int) client.count(request, RequestOptions.DEFAULT).getCount(); + return (int) client.count(request).count(); } catch (IOException e) { LOG.error("Search error", e); return 0; } } - protected List<SortBuilder<?>> sortBuilders( + protected List<SortOptions> sortBuilders( final AnyTypeKind kind, final List<OrderByClause> orderBy) { AnyUtils anyUtils = anyUtilsFactory.getInstance(kind); - List<SortBuilder<?>> builders = new ArrayList<>(); + List<SortOptions> options = new ArrayList<>(); orderBy.forEach(clause -> { String sortName = null; @@ -228,10 +238,16 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { if (sortName == null) { LOG.warn("Cannot build any valid clause from {}", clause); } else { - builders.add(new FieldSortBuilder(sortName).order(SortOrder.valueOf(clause.getDirection().name()))); + options.add(new SortOptions.Builder().field( + new FieldSort.Builder(). + field(sortName). + order(clause.getDirection() == OrderByClause.Direction.ASC + ? SortOrder.Asc : SortOrder.Desc). + build()). + build()); } }); - return builders; + return options; } @Override @@ -243,229 +259,252 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { final List<OrderByClause> orderBy, final AnyTypeKind kind) { - SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(). - query(getQueryBuilder(adminRealms, cond, kind)). + SearchRequest request = new SearchRequest.Builder(). + index(ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), kind)). + searchType(SearchType.QueryThenFetch). + query(getQuery(adminRealms, cond, kind)). from(itemsPerPage * (page <= 0 ? 0 : page - 1)). - size(itemsPerPage < 0 ? elasticsearchUtils.getIndexMaxResultWindow() : itemsPerPage); - sortBuilders(kind, orderBy).forEach(sourceBuilder::sort); - - SearchRequest request = new SearchRequest( - ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), kind)). - searchType(SearchType.QUERY_THEN_FETCH). - source(sourceBuilder); + size(itemsPerPage < 0 ? elasticsearchUtils.getIndexMaxResultWindow() : itemsPerPage). + sort(sortBuilders(kind, orderBy)). + build(); - SearchHit[] esResult = null; + @SuppressWarnings("rawtypes") + List<Hit<Map>> esResult = null; try { - esResult = client.search(request, RequestOptions.DEFAULT).getHits().getHits(); + esResult = client.search(request, Map.class).hits().hits(); } catch (Exception e) { LOG.error("While searching in Elasticsearch", e); } - return ArrayUtils.isEmpty(esResult) + return CollectionUtils.isEmpty(esResult) ? List.of() - : buildResult(Stream.of(esResult).map(SearchHit::getId).collect(Collectors.toList()), kind); + : buildResult(esResult.stream().map(Hit::id).collect(Collectors.toList()), kind); } - protected QueryBuilder getQueryBuilder(final SearchCond cond, final AnyTypeKind kind) { - QueryBuilder builder = null; + protected Query getQuery(final SearchCond cond, final AnyTypeKind kind) { + Query query = null; switch (cond.getType()) { case LEAF: case NOT_LEAF: - builder = cond.getLeaf(AnyTypeCond.class). + query = cond.getLeaf(AnyTypeCond.class). filter(leaf -> AnyTypeKind.ANY_OBJECT == kind). - map(this::getQueryBuilder). + map(this::getQuery). orElse(null); - if (builder == null) { - builder = cond.getLeaf(RelationshipTypeCond.class). + if (query == null) { + query = cond.getLeaf(RelationshipTypeCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). - map(this::getQueryBuilder). + map(this::getQuery). orElse(null); } - if (builder == null) { - builder = cond.getLeaf(RelationshipCond.class). + if (query == null) { + query = cond.getLeaf(RelationshipCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). - map(this::getQueryBuilder). + map(this::getQuery). orElse(null); } - if (builder == null) { - builder = cond.getLeaf(MembershipCond.class). + if (query == null) { + query = cond.getLeaf(MembershipCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). - map(this::getQueryBuilder). + map(this::getQuery). orElse(null); } - if (builder == null) { - builder = cond.getLeaf(MemberCond.class). + if (query == null) { + query = cond.getLeaf(MemberCond.class). filter(leaf -> AnyTypeKind.GROUP == kind). - map(this::getQueryBuilder). + map(this::getQuery). orElse(null); } - if (builder == null) { - builder = cond.getLeaf(AssignableCond.class). - map(this::getQueryBuilder). + if (query == null) { + query = cond.getLeaf(AssignableCond.class). + map(this::getQuery). orElse(null); } - if (builder == null) { - builder = cond.getLeaf(RoleCond.class). + if (query == null) { + query = cond.getLeaf(RoleCond.class). filter(leaf -> AnyTypeKind.USER == kind). - map(this::getQueryBuilder). + map(this::getQuery). orElse(null); } - if (builder == null) { - builder = cond.getLeaf(PrivilegeCond.class). + if (query == null) { + query = cond.getLeaf(PrivilegeCond.class). filter(leaf -> AnyTypeKind.USER == kind). - map(this::getQueryBuilder). + map(this::getQuery). orElse(null); } - if (builder == null) { - builder = cond.getLeaf(DynRealmCond.class). - map(this::getQueryBuilder). + if (query == null) { + query = cond.getLeaf(DynRealmCond.class). + map(this::getQuery). orElse(null); } - if (builder == null) { - builder = cond.getLeaf(ResourceCond.class). - map(this::getQueryBuilder). + if (query == null) { + query = cond.getLeaf(ResourceCond.class). + map(this::getQuery). orElse(null); } - if (builder == null) { + if (query == null) { Optional<AnyCond> anyCond = cond.getLeaf(AnyCond.class); if (anyCond.isPresent()) { - builder = getQueryBuilder(anyCond.get(), kind); + query = getQuery(anyCond.get(), kind); } else { - builder = cond.getLeaf(AttrCond.class). - map(leaf -> getQueryBuilder(leaf, kind)). + query = cond.getLeaf(AttrCond.class). + map(leaf -> getQuery(leaf, kind)). orElse(null); } } // allow for additional search conditions - if (builder == null) { - builder = getQueryBuilderForCustomConds(cond, kind); + if (query == null) { + query = getQueryForCustomConds(cond, kind); } - if (builder == null) { - builder = MATCH_NONE_QUERY_BUILDER; + if (query == null) { + query = MATCH_NONE_QUERY; } if (cond.getType() == SearchCond.Type.NOT_LEAF) { - builder = QueryBuilders.boolQuery().mustNot(builder); + query = new Query.Builder().bool(QueryBuilders.bool().mustNot(query).build()).build(); } break; case AND: - builder = QueryBuilders.boolQuery(). - must(getQueryBuilder(cond.getLeft(), kind)). - must(getQueryBuilder(cond.getRight(), kind)); + query = new Query.Builder().bool(QueryBuilders.bool(). + must(getQuery(cond.getLeft(), kind)).must(getQuery(cond.getRight(), kind)).build()). + build(); break; case OR: - builder = QueryBuilders.disMaxQuery(). - add(getQueryBuilder(cond.getLeft(), kind)). - add(getQueryBuilder(cond.getRight(), kind)); + query = new Query.Builder().disMax(QueryBuilders.disMax(). + queries(getQuery(cond.getLeft(), kind), getQuery(cond.getRight(), kind)).build()). + build(); break; default: } - return builder; + return query; } - protected QueryBuilder getQueryBuilder(final AnyTypeCond cond) { - return QueryBuilders.termQuery("anyType", cond.getAnyTypeKey()); + protected Query getQuery(final AnyTypeCond cond) { + return new Query.Builder().term(QueryBuilders.term(). + field("anyType").value(FieldValue.of(cond.getAnyTypeKey())).build()). + build(); } - protected QueryBuilder getQueryBuilder(final RelationshipTypeCond cond) { - return QueryBuilders.termQuery("relationshipTypes", cond.getRelationshipTypeKey()); + protected Query getQuery(final RelationshipTypeCond cond) { + return new Query.Builder().term(QueryBuilders.term(). + field("relationshipTypes").value(FieldValue.of(cond.getRelationshipTypeKey())).build()). + build(); } - protected QueryBuilder getQueryBuilder(final RelationshipCond cond) { + protected Query getQuery(final RelationshipCond cond) { String rightAnyObjectKey; try { rightAnyObjectKey = check(cond); } catch (IllegalArgumentException e) { - return MATCH_NONE_QUERY_BUILDER; + return MATCH_NONE_QUERY; } - return QueryBuilders.termQuery("relationships", rightAnyObjectKey); + return new Query.Builder().term(QueryBuilders.term(). + field("relationships").value(FieldValue.of(rightAnyObjectKey)).build()). + build(); } - protected QueryBuilder getQueryBuilder(final MembershipCond cond) { + protected Query getQuery(final MembershipCond cond) { List<String> groupKeys; try { groupKeys = check(cond); } catch (IllegalArgumentException e) { - return MATCH_NONE_QUERY_BUILDER; + return MATCH_NONE_QUERY; } - if (groupKeys.size() == 1) { - return QueryBuilders.termQuery("memberships", groupKeys.get(0)); + List<Query> membershipQueries = groupKeys.stream(). + map(key -> new Query.Builder().term(QueryBuilders.term(). + field("memberships").value(FieldValue.of(key)).build()). + build()).collect(Collectors.toList()); + if (membershipQueries.size() == 1) { + return membershipQueries.get(0); } - DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery(); - groupKeys.forEach(key -> builder.add(QueryBuilders.termQuery("memberships", key))); - return builder; + return new Query.Builder().disMax(QueryBuilders.disMax().queries(membershipQueries).build()).build(); } - protected QueryBuilder getQueryBuilder(final AssignableCond cond) { + protected Query getQuery(final AssignableCond cond) { Realm realm; try { realm = check(cond); } catch (IllegalArgumentException e) { - return MATCH_NONE_QUERY_BUILDER; + return MATCH_NONE_QUERY; } - DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery(); + List<Query> queries = new ArrayList<>(); if (cond.isFromGroup()) { realmDAO.findDescendants(realm).forEach( - current -> builder.add(QueryBuilders.termQuery("realm", current.getFullPath()))); + current -> queries.add(new Query.Builder().term(QueryBuilders.term(). + field("realm").value(FieldValue.of(current.getFullPath())).build()). + build())); } else { for (Realm current = realm; current.getParent() != null; current = current.getParent()) { - builder.add(QueryBuilders.termQuery("realm", current.getFullPath())); + queries.add(new Query.Builder().term(QueryBuilders.term(). + field("realm").value(FieldValue.of(current.getFullPath())).build()). + build()); } - builder.add(QueryBuilders.termQuery("realm", realmDAO.getRoot().getFullPath())); + queries.add(new Query.Builder().term(QueryBuilders.term(). + field("realm").value(FieldValue.of(realmDAO.getRoot().getFullPath())).build()). + build()); } - return builder; + return new Query.Builder().disMax(QueryBuilders.disMax().queries(queries).build()).build(); } - protected QueryBuilder getQueryBuilder(final RoleCond cond) { - return QueryBuilders.termQuery("roles", cond.getRole()); + protected Query getQuery(final RoleCond cond) { + return new Query.Builder().term(QueryBuilders.term(). + field("roles").value(FieldValue.of(cond.getRole())).build()). + build(); } - protected QueryBuilder getQueryBuilder(final PrivilegeCond cond) { - return QueryBuilders.termQuery("privileges", cond.getPrivilege()); + protected Query getQuery(final PrivilegeCond cond) { + return new Query.Builder().term(QueryBuilders.term(). + field("privileges").value(FieldValue.of(cond.getPrivilege())).build()). + build(); } - protected QueryBuilder getQueryBuilder(final DynRealmCond cond) { - return QueryBuilders.termQuery("dynRealms", cond.getDynRealm()); + protected Query getQuery(final DynRealmCond cond) { + return new Query.Builder().term(QueryBuilders.term(). + field("dynRealms").value(FieldValue.of(cond.getDynRealm())).build()). + build(); } - protected QueryBuilder getQueryBuilder(final MemberCond cond) { + protected Query getQuery(final MemberCond cond) { String memberKey; try { memberKey = check(cond); } catch (IllegalArgumentException e) { - return MATCH_NONE_QUERY_BUILDER; + return MATCH_NONE_QUERY; } - return QueryBuilders.termQuery("members", memberKey); + return new Query.Builder().term(QueryBuilders.term(). + field("members").value(FieldValue.of(memberKey)).build()). + build(); } - protected QueryBuilder getQueryBuilder(final ResourceCond cond) { - return QueryBuilders.termQuery("resources", cond.getResourceKey()); + protected Query getQuery(final ResourceCond cond) { + return new Query.Builder().term(QueryBuilders.term(). + field("resources").value(FieldValue.of(cond.getResourceKey())).build()). + build(); } - protected QueryBuilder fillAttrQuery( + protected Query fillAttrQuery( final PlainSchema schema, final PlainAttrValue attrValue, final AttrCond cond) { @@ -474,15 +513,17 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { ? attrValue.getDateValue().getTime() : attrValue.getValue(); - QueryBuilder builder = MATCH_NONE_QUERY_BUILDER; + Query query = MATCH_NONE_QUERY; switch (cond.getType()) { case ISNOTNULL: - builder = QueryBuilders.existsQuery(schema.getKey()); + query = new Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build()).build(); break; case ISNULL: - builder = QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(schema.getKey())); + query = new Query.Builder().bool(QueryBuilders.bool().mustNot( + new Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build()).build()). + build()).build(); break; case ILIKE: @@ -499,62 +540,86 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { output.append(escapeForLikeRegex(c)); } } - builder = QueryBuilders.regexpQuery(schema.getKey(), output.toString()); + query = new Query.Builder().regexp(QueryBuilders.regexp(). + field(schema.getKey()).value(output.toString()).build()).build(); break; case LIKE: - builder = QueryBuilders.wildcardQuery(schema.getKey(), cond.getExpression().replace('%', '*')); + query = new Query.Builder().wildcard(QueryBuilders.wildcard(). + field(schema.getKey()).value(cond.getExpression().replace('%', '*')).build()).build(); break; case IEQ: - builder = QueryBuilders.matchQuery(schema.getKey(), cond.getExpression().toLowerCase()); + query = new Query.Builder().match(QueryBuilders.match(). + field(schema.getKey()).query(FieldValue.of(cond.getExpression().toLowerCase())).build()). + build(); break; case EQ: - builder = QueryBuilders.termQuery(schema.getKey(), value); + FieldValue fieldValue; + if (value instanceof Double) { + fieldValue = FieldValue.of((Double) value); + } else if (value instanceof Long) { + fieldValue = FieldValue.of((Long) value); + } else if (value instanceof Boolean) { + fieldValue = FieldValue.of((Boolean) value); + } else { + fieldValue = FieldValue.of(value.toString()); + } + query = new Query.Builder().term(QueryBuilders.term(). + field(schema.getKey()).value(fieldValue).build()). + build(); break; case GE: - builder = QueryBuilders.rangeQuery(schema.getKey()).gte(value); + query = new Query.Builder().range(QueryBuilders.range(). + field(schema.getKey()).gte(JsonData.of(value)).build()). + build(); break; case GT: - builder = QueryBuilders.rangeQuery(schema.getKey()).gt(value); + query = new Query.Builder().range(QueryBuilders.range(). + field(schema.getKey()).gt(JsonData.of(value)).build()). + build(); break; case LE: - builder = QueryBuilders.rangeQuery(schema.getKey()).lte(value); + query = new Query.Builder().range(QueryBuilders.range(). + field(schema.getKey()).lte(JsonData.of(value)).build()). + build(); break; case LT: - builder = QueryBuilders.rangeQuery(schema.getKey()).lt(value); + query = new Query.Builder().range(QueryBuilders.range(). + field(schema.getKey()).lt(JsonData.of(value)).build()). + build(); break; default: } - return builder; + return query; } - protected QueryBuilder getQueryBuilder(final AttrCond cond, final AnyTypeKind kind) { + protected Query getQuery(final AttrCond cond, final AnyTypeKind kind) { Pair<PlainSchema, PlainAttrValue> checked; try { checked = check(cond, kind); } catch (IllegalArgumentException e) { - return MATCH_NONE_QUERY_BUILDER; + return MATCH_NONE_QUERY; } return fillAttrQuery(checked.getLeft(), checked.getRight(), cond); } - protected QueryBuilder getQueryBuilder(final AnyCond cond, final AnyTypeKind kind) { + protected Query getQuery(final AnyCond cond, final AnyTypeKind kind) { if (JAXRSService.PARAM_REALM.equals(cond.getSchema()) && SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) { Realm realm = realmDAO.find(cond.getExpression()); if (realm == null) { LOG.warn("Invalid Realm key: {}", cond.getExpression()); - return MATCH_NONE_QUERY_BUILDER; + return MATCH_NONE_QUERY; } cond.setExpression(realm.getFullPath()); } @@ -563,13 +628,13 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO { try { checked = check(cond, kind); } catch (IllegalArgumentException e) { - return MATCH_NONE_QUERY_BUILDER; + return MATCH_NONE_QUERY; } return fillAttrQuery(checked.getLeft(), checked.getMiddle(), checked.getRight()); } - protected QueryBuilder getQueryBuilderForCustomConds(final SearchCond cond, final AnyTypeKind kind) { - return MATCH_ALL_QUERY_BUILDER; + protected Query getQueryForCustomConds(final SearchCond cond, final AnyTypeKind kind) { + return MATCH_ALL_QUERY; } } diff --git a/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java b/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java index 2fa9a54..770cb76 100644 --- a/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java +++ b/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java @@ -18,11 +18,17 @@ */ package org.apache.syncope.core.persistence.jpa.dao; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import co.elastic.clients.elasticsearch._types.FieldValue; +import co.elastic.clients.elasticsearch._types.SearchType; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders; +import co.elastic.clients.elasticsearch.core.SearchRequest; import java.io.IOException; import java.util.List; import java.util.Optional; @@ -48,11 +54,6 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser; 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.SearchRequest; -import org.elasticsearch.action.search.SearchType; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.search.builder.SearchSourceBuilder; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -87,7 +88,7 @@ public class ElasticsearchAnySearchDAOTest { private ElasticsearchAnySearchDAO searchDAO; @Test - public void getAdminRealmsFilter_realm() { + public void getAdminRealmsFilter_realm() throws IOException { // 1. mock Realm root = mock(Realm.class); when(root.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM); @@ -97,11 +98,15 @@ public class ElasticsearchAnySearchDAOTest { // 2. test Set<String> adminRealms = Set.of(SyncopeConstants.ROOT_REALM); - Triple<Optional<QueryBuilder>, Set<String>, Set<String>> filter = + Triple<Optional<Query>, Set<String>, Set<String>> filter = searchDAO.getAdminRealmsFilter(AnyTypeKind.USER, adminRealms); - assertEquals( - QueryBuilders.disMaxQuery().add(QueryBuilders.termQuery("realm", SyncopeConstants.ROOT_REALM)), - filter.getLeft().get()); + + assertThat( + new Query.Builder().disMax(QueryBuilders.disMax().queries( + new Query.Builder().term(QueryBuilders.term().field("realm").value( + FieldValue.of(SyncopeConstants.ROOT_REALM)).build()).build()).build()). + build()). + usingRecursiveComparison().isEqualTo(filter.getLeft().get()); assertEquals(Set.of(), filter.getMiddle()); assertEquals(Set.of(), filter.getRight()); } @@ -116,7 +121,7 @@ public class ElasticsearchAnySearchDAOTest { // 2. test Set<String> adminRealms = Set.of("dyn"); - Triple<Optional<QueryBuilder>, Set<String>, Set<String>> filter = + Triple<Optional<Query>, Set<String>, Set<String>> filter = searchDAO.getAdminRealmsFilter(AnyTypeKind.USER, adminRealms); assertFalse(filter.getLeft().isPresent()); assertEquals(Set.of("dyn"), filter.getMiddle()); @@ -126,7 +131,7 @@ public class ElasticsearchAnySearchDAOTest { @Test public void getAdminRealmsFilter_groupOwner() { Set<String> adminRealms = Set.of(RealmUtils.getGroupOwnerRealm("/any", "groupKey")); - Triple<Optional<QueryBuilder>, Set<String>, Set<String>> filter = + Triple<Optional<Query>, Set<String>, Set<String>> filter = searchDAO.getAdminRealmsFilter(AnyTypeKind.USER, adminRealms); assertFalse(filter.getLeft().isPresent()); assertEquals(Set.of(), filter.getMiddle()); @@ -156,22 +161,21 @@ public class ElasticsearchAnySearchDAOTest { AnyCond anyCond = new AnyCond(AttrCond.Type.ISNOTNULL); anyCond.setSchema("id"); - SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(). - query(searchDAO.getQueryBuilder(adminRealms, SearchCond.getLeaf(anyCond), AnyTypeKind.USER)). + SearchRequest request = new SearchRequest.Builder(). + index(ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), AnyTypeKind.USER)). + searchType(SearchType.QueryThenFetch). + query(searchDAO.getQuery(adminRealms, SearchCond.getLeaf(anyCond), AnyTypeKind.USER)). from(1). - size(10); - searchDAO.sortBuilders(AnyTypeKind.USER, List.of()).forEach(sourceBuilder::sort); - - SearchRequest request = new SearchRequest( - ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), AnyTypeKind.USER)). - searchType(SearchType.QUERY_THEN_FETCH). - source(sourceBuilder); - - assertEquals( - QueryBuilders.boolQuery(). - must(QueryBuilders.existsQuery("id")). - must(QueryBuilders.termQuery("memberships", "groupKey")), - request.source().query()); + size(10). + build(); + + assertThat( + new Query.Builder().bool(QueryBuilders.bool(). + must(new Query.Builder().exists(QueryBuilders.exists().field("id").build()).build()). + must(new Query.Builder().term(QueryBuilders.term().field("memberships").value( + FieldValue.of("groupKey")).build()).build()). + build()).build()). + usingRecursiveComparison().isEqualTo(request.query()); } } } diff --git a/ext/elasticsearch/provisioning-java/pom.xml b/ext/elasticsearch/provisioning-java/pom.xml index ef60198..bd1d9c8 100644 --- a/ext/elasticsearch/provisioning-java/pom.xml +++ b/ext/elasticsearch/provisioning-java/pom.xml @@ -43,7 +43,7 @@ under the License. <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> 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 6ae2d34..f7ef590 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,7 +18,13 @@ */ package org.apache.syncope.core.provisioning.java.job; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.mapping.TypeMapping; +import co.elastic.clients.elasticsearch.core.IndexRequest; +import co.elastic.clients.elasticsearch.core.IndexResponse; +import co.elastic.clients.elasticsearch.indices.IndexSettings; import java.io.IOException; +import java.util.Map; 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; @@ -28,11 +34,6 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager; import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.beans.factory.annotation.Autowired; @@ -43,7 +44,7 @@ import org.springframework.beans.factory.annotation.Autowired; public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate { @Autowired - protected RestHighLevelClient client; + protected ElasticsearchClient client; @Autowired protected ElasticsearchIndexManager indexManager; @@ -60,27 +61,27 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate { @Autowired protected AnyObjectDAO anyObjectDAO; - protected XContentBuilder userSettings() throws IOException { + protected IndexSettings userSettings() throws IOException { return indexManager.defaultSettings(); } - protected XContentBuilder groupSettings() throws IOException { + protected IndexSettings groupSettings() throws IOException { return indexManager.defaultSettings(); } - protected XContentBuilder anyObjectSettings() throws IOException { + protected IndexSettings anyObjectSettings() throws IOException { return indexManager.defaultSettings(); } - protected XContentBuilder userMapping() throws IOException { + protected TypeMapping userMapping() throws IOException { return indexManager.defaultMapping(); } - protected XContentBuilder groupMapping() throws IOException { + protected TypeMapping groupMapping() throws IOException { return indexManager.defaultMapping(); } - protected XContentBuilder anyObjectMapping() throws IOException { + protected TypeMapping anyObjectMapping() throws IOException { return indexManager.defaultMapping(); } @@ -104,13 +105,14 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate { LOG.debug("Indexing users..."); for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) { for (String user : userDAO.findAllKeys(page, AnyDAO.DEFAULT_PAGE_SIZE)) { - IndexRequest request = new IndexRequest( - ElasticsearchUtils.getContextDomainName( + IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>(). + index(ElasticsearchUtils.getContextDomainName( AuthContextUtils.getDomain(), AnyTypeKind.USER)). id(user). - source(utils.builder(userDAO.find(user), AuthContextUtils.getDomain())); + document(utils.document(userDAO.find(user), AuthContextUtils.getDomain())). + build(); try { - IndexResponse response = client.index(request, RequestOptions.DEFAULT); + IndexResponse response = client.index(request); LOG.debug("Index successfully created for {}: {}", user, response); } catch (Exception e) { LOG.error("Could not create index for {} {}", AnyTypeKind.USER, user); @@ -121,13 +123,14 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate { LOG.debug("Indexing groups..."); for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) { for (String group : groupDAO.findAllKeys(page, AnyDAO.DEFAULT_PAGE_SIZE)) { - IndexRequest request = new IndexRequest( - ElasticsearchUtils.getContextDomainName( + IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>(). + index(ElasticsearchUtils.getContextDomainName( AuthContextUtils.getDomain(), AnyTypeKind.GROUP)). id(group). - source(utils.builder(groupDAO.find(group), AuthContextUtils.getDomain())); + document(utils.document(groupDAO.find(group), AuthContextUtils.getDomain())). + build(); try { - IndexResponse response = client.index(request, RequestOptions.DEFAULT); + IndexResponse response = client.index(request); LOG.debug("Index successfully created for {}: {}", group, response); } catch (Exception e) { LOG.error("Could not create index for {} {}", AnyTypeKind.GROUP, group); @@ -138,13 +141,14 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate { LOG.debug("Indexing any objects..."); for (int page = 1; page <= (anyObjectDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) { for (String anyObject : anyObjectDAO.findAllKeys(page, AnyDAO.DEFAULT_PAGE_SIZE)) { - IndexRequest request = new IndexRequest( - ElasticsearchUtils.getContextDomainName( + IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>(). + index(ElasticsearchUtils.getContextDomainName( AuthContextUtils.getDomain(), AnyTypeKind.ANY_OBJECT)). id(anyObject). - source(utils.builder(anyObjectDAO.find(anyObject), AuthContextUtils.getDomain())); + document(utils.document(anyObjectDAO.find(anyObject), AuthContextUtils.getDomain())). + build(); try { - IndexResponse response = client.index(request, RequestOptions.DEFAULT); + IndexResponse response = client.index(request); LOG.debug("Index successfully created for {}: {}", anyObject, response); } catch (Exception e) { LOG.error("Could not create index for {} {}", AnyTypeKind.ANY_OBJECT, anyObject); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java index 5c7b501..dde15d4 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java @@ -167,12 +167,10 @@ public class NotificationTaskITCase extends AbstractNotificationTaskITCase { taskTO = taskService.read(TaskType.NOTIFICATION, taskTO.getKey(), true); assertNotNull(taskTO); assertTrue(taskTO.isExecuted()); - assertEquals(1, taskTO.getExecutions().size()); + assertFalse(taskTO.getExecutions().isEmpty()); } finally { // Remove execution to make test re-runnable - if (!taskTO.getExecutions().isEmpty()) { - taskService.deleteExecution(taskTO.getExecutions().get(0).getKey()); - } + taskTO.getExecutions().forEach(e -> taskService.deleteExecution(e.getKey())); } } diff --git a/pom.xml b/pom.xml index 6e364c4..00acfdc 100644 --- a/pom.xml +++ b/pom.xml @@ -430,7 +430,7 @@ under the License. <slf4j.version>1.7.32</slf4j.version> - <elasticsearch.version>7.15.2</elasticsearch.version> + <elasticsearch.version>7.16.2</elasticsearch.version> <apacheds.version>2.0.0.AM26</apacheds.version> <apachedirapi.version>2.0.0</apachedirapi.version> @@ -677,33 +677,19 @@ under the License. <!-- /Camel --> <dependency> - <groupId>org.elasticsearch.client</groupId> - <artifactId>elasticsearch-rest-high-level-client</artifactId> - <version>${elasticsearch.version}</version> - <exclusions> - <exclusion> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-dataformat-smile</artifactId> - </exclusion> - <exclusion> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-dataformat-yaml</artifactId> - </exclusion> - <exclusion> - <groupId>com.fasterxml.jackson.datatype</groupId> - <artifactId>jackson-dataformat-cbor</artifactId> - </exclusion> - </exclusions> + <groupId>co.elastic.clients</groupId> + <artifactId>elasticsearch-java</artifactId> + <version>${elasticsearch.version}</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> - <version>${elasticsearch.version}</version> + <version>7.16.1</version> </dependency> <dependency> - <groupId>org.elasticsearch</groupId> - <artifactId>elasticsearch</artifactId> - <version>${elasticsearch.version}</version> + <groupId>jakarta.json</groupId> + <artifactId>jakarta.json-api</artifactId> + <version>2.0.1</version> </dependency> <dependency>