Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Currently this will be a `org
.springframework.data.elasticsearch.core.clients.elasticsearch7.ElasticsearchAggregations` object; later different implementations will be available.
The same change has been done to the `ReactiveSearchOperations.aggregate()` functions, the now return a `Flux<AggregationContainer<?>>`.
Programs using the aggregations need to be changed to cast the returned value to the appropriate class to further proces it.
* methods that might have thrown a `org.elasticsearch.ElasticsearchStatusException` now will throw `org.springframework.data.elasticsearch.RestStatusException` instead.

=== Handling of field and sourceFilter properties of Query

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed 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
*
* https://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.springframework.data.elasticsearch;

import org.springframework.dao.DataAccessException;

/**
* Exception class for REST status exceptions independent from the used client/backend.
*
* @author Peter-Josef Meisch
* @since 4.3
*/
public class RestStatusException extends DataAccessException {

// we do not use a dedicated status class from Elasticsearch, OpenSearch, Spring web or webflux here
private final int status;

public RestStatusException(int status, String msg) {
super(msg);
this.status = status;
}

public RestStatusException(int status, String msg, Throwable cause) {
super(msg, cause);
this.status = status;
}

public int getStatus() {
return status;
}

@Override
public String toString() {
return "RestStatusException{" + "status=" + status + "} " + super.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.suggest.Suggest;
import org.reactivestreams.Publisher;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.ClientLogger;
Expand Down Expand Up @@ -836,8 +837,7 @@ private static <T> Mono<T> doDecode(ClientResponse response, Class<T> responseTy
return Mono.error(BytesRestResponse.errorFromXContent(createParser(mediaType, content)));
} catch (Exception e) {

return Mono
.error(new ElasticsearchStatusException(content, RestStatus.fromCode(response.statusCode().value())));
return Mono.error(new RestStatusException(response.statusCode().value(), content));
}
}
}
Expand Down Expand Up @@ -870,14 +870,14 @@ private <T> Publisher<? extends T> handleServerError(Request request, ClientResp
String mediaType = response.headers().contentType().map(MediaType::toString).orElse(XContentType.JSON.mediaType());

return response.body(BodyExtractors.toMono(byte[].class)) //
.switchIfEmpty(Mono.error(
new ElasticsearchStatusException(String.format("%s request to %s returned error code %s and no body.",
request.getMethod(), request.getEndpoint(), statusCode), status)))
.switchIfEmpty(Mono.error(new RestStatusException(status.getStatus(),
String.format("%s request to %s returned error code %s and no body.", request.getMethod(),
request.getEndpoint(), statusCode))))
.map(bytes -> new String(bytes, StandardCharsets.UTF_8)) //
.flatMap(content -> contentOrError(content, mediaType, status))
.flatMap(unused -> Mono
.error(new ElasticsearchStatusException(String.format("%s request to %s returned error code %s.",
request.getMethod(), request.getEndpoint(), statusCode), status)));
.error(new RestStatusException(status.getStatus(), String.format("%s request to %s returned error code %s.",
request.getMethod(), request.getEndpoint(), statusCode))));
}

private <T> Publisher<? extends T> handleClientError(String logId, ClientResponse response, Class<T> responseType) {
Expand Down Expand Up @@ -909,7 +909,7 @@ private static Mono<String> contentOrError(String content, String mediaType, Res
if (exception != null) {
StringBuilder sb = new StringBuilder();
buildExceptionMessages(sb, exception);
return Mono.error(new ElasticsearchStatusException(sb.toString(), status, exception));
return Mono.error(new RestStatusException(status.getStatus(), sb.toString(), exception));
}

return Mono.just(content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.common.ValidationException;
import org.elasticsearch.index.engine.VersionConflictEngineException;
import org.elasticsearch.rest.RestStatus;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.elasticsearch.NoSuchIndexException;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.UncategorizedElasticsearchException;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
Expand All @@ -38,7 +38,7 @@
* Simple {@link PersistenceExceptionTranslator} for Elasticsearch. Convert the given runtime exception to an
* appropriate exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is
* appropriate: any other exception may have resulted from user code, and should not be translated.
*
*
* @author Christoph Strobl
* @author Peter-Josef Meisch
* @author Roman Puchkovskiy
Expand All @@ -63,9 +63,28 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
ex);
}

if (elasticsearchException instanceof ElasticsearchStatusException) {
ElasticsearchStatusException restStatusException = (ElasticsearchStatusException) elasticsearchException;
return new RestStatusException(restStatusException.status().getStatus(), restStatusException.getMessage(),
restStatusException.getCause());
}

return new UncategorizedElasticsearchException(ex.getMessage(), ex);
}

if (ex instanceof RestStatusException) {
RestStatusException restStatusException = (RestStatusException) ex;
Throwable cause = restStatusException.getCause();
if (cause instanceof ElasticsearchException) {
ElasticsearchException elasticsearchException = (ElasticsearchException) cause;

if (!indexAvailable(elasticsearchException)) {
return new NoSuchIndexException(ObjectUtils.nullSafeToString(elasticsearchException.getMetadata("es.index")),
ex);
}
}
}

if (ex instanceof ValidationException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
Expand All @@ -80,13 +99,26 @@ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {

private boolean isSeqNoConflict(Exception exception) {

Integer status = null;
String message = null;

if (exception instanceof ElasticsearchStatusException) {

ElasticsearchStatusException statusException = (ElasticsearchStatusException) exception;
status = statusException.status().getStatus();
message = statusException.getMessage();
}

if (exception instanceof RestStatusException) {

RestStatusException statusException = (RestStatusException) exception;
status = statusException.getStatus();
message = statusException.getMessage();
}

return statusException.status() == RestStatus.CONFLICT && statusException.getMessage() != null
&& statusException.getMessage().contains("type=version_conflict_engine_exception")
&& statusException.getMessage().contains("version conflict, required seqNo");
if (status != null && message != null) {
return status == 409 && message.contains("type=version_conflict_engine_exception")
&& message.contains("version conflict, required seqNo");
}

if (exception instanceof VersionConflictEngineException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.util.Optional;
import java.util.function.Function;

import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.client.Request;
Expand All @@ -40,6 +39,7 @@
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
Expand Down Expand Up @@ -84,8 +84,8 @@ public Mono<ResponseSpec> execute(ReactiveElasticsearchClientCallback callback)
}

@Test // #1712
@DisplayName("should throw ElasticsearchStatusException on server 5xx with empty body")
void shouldThrowElasticsearchStatusExceptionOnServer5xxWithEmptyBody() {
@DisplayName("should throw RestStatusException on server 5xx with empty body")
void shouldThrowRestStatusExceptionOnServer5xxWithEmptyBody() {

when(hostProvider.getActive(any())).thenReturn(Mono.just(webClient));
WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class);
Expand All @@ -108,7 +108,7 @@ void shouldThrowElasticsearchStatusExceptionOnServer5xxWithEmptyBody() {

client.get(new GetRequest("42")) //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class) //
.expectError(RestStatusException.class) //
.verify(); //
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import java.util.UUID;
import java.util.stream.IntStream;

import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
Expand Down Expand Up @@ -63,6 +62,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.RestStatusException;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ReactiveIndexOperations;
Expand Down Expand Up @@ -165,7 +165,7 @@ public void getOnNonExistingIndexShouldThrowException() {

client.get(new GetRequest(INDEX_I, "nonono")) //
.as(StepVerifier::create) //
.expectError(ElasticsearchStatusException.class) //
.expectError(RestStatusException.class) //
.verify();
}

Expand Down Expand Up @@ -356,7 +356,7 @@ public void indexShouldErrorForExistingDocuments() {

client.index(request) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-488
Expand Down Expand Up @@ -405,7 +405,7 @@ public void updateShouldErrorNonExistingDocumentWhenNotUpserted() {

client.update(request) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-488
Expand Down Expand Up @@ -715,7 +715,7 @@ public void createExistingIndexErrors() {

client.indices().createIndex(request -> request.index(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // #1658
Expand All @@ -737,7 +737,7 @@ public void createExistingIndexErrors_() {

client.indices().createIndex(new CreateIndexRequest(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // #1658
Expand All @@ -757,7 +757,7 @@ public void getIndexError() {
operations.indexOps(IndexCoordinates.of(INDEX_I)).create().block();

client.indices().getIndex(new GetIndexRequest(INDEX_II)).as(StepVerifier::create)
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-569, DATAES-678
Expand All @@ -782,7 +782,7 @@ public void deleteNonExistingIndexErrors() {

client.indices().deleteIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-569
Expand All @@ -800,7 +800,7 @@ public void openNonExistingIndex() {

client.indices().openIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-569
Expand All @@ -818,7 +818,7 @@ public void closeNonExistingIndex() {

client.indices().closeIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-569
Expand All @@ -836,7 +836,7 @@ public void refreshNonExistingIndex() {

client.indices().refreshIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // #1640
Expand Down Expand Up @@ -869,7 +869,7 @@ void putMappingError() {

client.indices().putMapping(putMappingRequest) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // #1640
Expand Down Expand Up @@ -911,7 +911,7 @@ void getMappingError() {

client.indices().getMapping(getMappingsRequest) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-569
Expand All @@ -936,7 +936,7 @@ public void updateMappingNonExistingIndex() {

client.indices().putMapping(request -> request.indices(INDEX_I).source(jsonMap)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-569
Expand All @@ -954,7 +954,7 @@ public void flushNonExistingIndex() {

client.indices().flushIndex(request -> request.indices(INDEX_I)) //
.as(StepVerifier::create) //
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-684
Expand Down Expand Up @@ -1076,7 +1076,7 @@ void getFieldMappingNonExistingField() {
void getFieldMappingNonExistingIndex() {

client.indices().getFieldMapping(request -> request.indices(INDEX_I).fields("message1")).as(StepVerifier::create)
.verifyError(ElasticsearchStatusException.class);
.verifyError(RestStatusException.class);
}

@Test // DATAES-796
Expand Down
Loading