/* * Hibernate Search, full-text search for your domain model * * License: GNU Lesser General Public License (LGPL), version 2.1 or later * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.search.elasticsearch.work.impl; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.elasticsearch.client.Response; import org.hibernate.search.elasticsearch.client.impl.ElasticsearchRequest; import org.hibernate.search.elasticsearch.gson.impl.GsonProvider; import org.hibernate.search.elasticsearch.gson.impl.JsonAccessor; import org.hibernate.search.elasticsearch.logging.impl.Log; import org.hibernate.search.elasticsearch.util.impl.ElasticsearchClientUtils; import org.hibernate.search.exception.SearchException; import org.hibernate.search.util.logging.impl.LoggerFactory; import com.google.gson.JsonObject; /** * @author Yoann Rodiere */ public class DefaultElasticsearchRequestSuccessAssessor implements ElasticsearchRequestSuccessAssessor { private static final Log LOG = LoggerFactory.make( Log.class ); private static final JsonAccessor ROOT_ERROR_TYPE = JsonAccessor.root().property( "error" ).property( "type" ); private static final JsonAccessor BULK_ITEM_STATUS_CODE = JsonAccessor.root().property( "status" ); private static final JsonAccessor BULK_ITEM_ERROR_TYPE = JsonAccessor.root().property( "error" ).property( "type" ); private static final int TIME_OUT_HTTP_STATUS_CODE = 408; public static final DefaultElasticsearchRequestSuccessAssessor INSTANCE = builder().build(); public static Builder builder() { return new Builder(); } public static class Builder { private final Set<Integer> ignoredErrorStatuses = new HashSet<>(); private final Set<String> ignoredErrorTypes = new HashSet<>(); public Builder ignoreErrorStatuses(int ... ignoredErrorStatuses) { for ( int ignoredErrorStatus : ignoredErrorStatuses ) { this.ignoredErrorStatuses.add( ignoredErrorStatus ); } return this; } public Builder ignoreErrorTypes(String ... ignoredErrorTypes) { for ( String ignoredErrorType : ignoredErrorTypes ) { this.ignoredErrorTypes.add( ignoredErrorType ); } return this; } public DefaultElasticsearchRequestSuccessAssessor build() { return new DefaultElasticsearchRequestSuccessAssessor( this ); } } private final Set<Integer> ignoredErrorStatuses; private final Set<String> ignoredErrorTypes; private DefaultElasticsearchRequestSuccessAssessor(Builder builder) { this.ignoredErrorStatuses = Collections.unmodifiableSet( new HashSet<>( builder.ignoredErrorStatuses ) ); this.ignoredErrorTypes = Collections.unmodifiableSet( new HashSet<>( builder.ignoredErrorTypes ) ); } @Override public String toString() { return new StringBuilder() .append( getClass().getSimpleName() ).append( "[" ) .append( "ignoredErrorStatuses=" ).append( ignoredErrorStatuses ) .append( ", ignoredErrorTypes=" ).append( ignoredErrorTypes ) .append( "]" ) .toString(); } @Override public void checkSuccess(ElasticsearchWorkExecutionContext context, ElasticsearchRequest request, Response response, JsonObject parsedResponseBody) throws SearchException { if ( !isSuccess( response, parsedResponseBody ) ) { GsonProvider gsonProvider = context.getGsonProvider(); if ( response.getStatusLine().getStatusCode() == TIME_OUT_HTTP_STATUS_CODE ) { throw LOG.elasticsearchRequestTimeout( ElasticsearchClientUtils.formatRequest( gsonProvider, request ), ElasticsearchClientUtils.formatResponse( gsonProvider, response, parsedResponseBody ) ); } else { throw LOG.elasticsearchRequestFailed( ElasticsearchClientUtils.formatRequest( gsonProvider, request ), ElasticsearchClientUtils.formatResponse( gsonProvider, response, parsedResponseBody ), null ); } } } @Override public boolean isSuccess(ElasticsearchWorkExecutionContext context, JsonObject resultItem) { if ( resultItem == null ) { return false; } // Result items have the following format: { "actionName" : { "status" : 201, ... } } JsonObject content = resultItem.entrySet().iterator().next().getValue().getAsJsonObject(); int statusCode = BULK_ITEM_STATUS_CODE.get( content ).getAsInt(); return ElasticsearchClientUtils.isSuccessCode( statusCode ) || ignoredErrorStatuses.contains( statusCode ) || ignoredErrorTypes.contains( BULK_ITEM_ERROR_TYPE.get( content ).getAsString() ); } private boolean isSuccess(Response response, JsonObject parsedResponseBody) { int code = response.getStatusLine().getStatusCode(); return ElasticsearchClientUtils.isSuccessCode( code ) || ignoredErrorStatuses.contains( code ) || ignoredErrorTypes.contains( ROOT_ERROR_TYPE.get( parsedResponseBody ).getAsString() ); } }