/*
* 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.test;
import static org.fest.assertions.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.apache.lucene.document.Document;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.FieldBridge;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.bridge.LuceneOptions;
import org.hibernate.search.cfg.Environment;
import org.hibernate.search.elasticsearch.ElasticsearchQueries;
import org.hibernate.search.elasticsearch.work.impl.BulkRequestFailedException;
import org.hibernate.search.exception.ErrorContext;
import org.hibernate.search.exception.ErrorHandler;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.query.engine.spi.QueryDescriptor;
import org.hibernate.search.test.SearchTestBase;
import org.junit.After;
import org.junit.Test;
/**
* Tests invocation of the error handler during indexing. For that, a malicious field bridge is used which writes
* unexpected fields for specific documents, causing these updates to fail.
*
* @author Gunnar Morling
*/
public class ElasticsearchExceptionHandlingIT extends SearchTestBase {
private static TestExceptionHandler errorHandler = new TestExceptionHandler();
@After
public void deleteTestDataAndResetErrorHandler() {
Session s = openSession();
FullTextSession session = Search.getFullTextSession( s );
Transaction tx = s.beginTransaction();
QueryDescriptor query = ElasticsearchQueries.fromJson( "{ 'query': { 'match_all' : {} } }" );
List<?> result = session.createFullTextQuery( query ).list();
for ( Object entity : result ) {
session.delete( entity );
}
tx.commit();
s.close();
errorHandler.reset();
}
@Test
public void errorHandlerInvokedForSingleOperation() throws Exception {
Session s = openSession();
FullTextSession session = Search.getFullTextSession( s );
Transaction tx = s.beginTransaction();
Actor bert = new Actor();
bert.id = 1L;
bert.name = "Bert";
s.persist( bert );
tx.commit();
assertThat( errorHandler.getHandleInvocations() ).hasSize( 1 );
ErrorContext errorContext = errorHandler.getHandleInvocations().iterator().next();
assertThat( errorContext.getThrowable() ).isExactlyInstanceOf( SearchException.class );
assertThat( errorContext.getFailingOperations() ).onProperty( "idInString" ).containsOnly( "1" );
tx = s.beginTransaction();
QueryDescriptor query = ElasticsearchQueries.fromJson( "{ 'query': { 'match_all' : {} } }" );
List<?> result = session.createFullTextQuery( query, Actor.class ).list();
assertThat( result ).isEmpty();
tx.commit();
s.close();
}
@Test
public void errorHandlerInvokedForBulk() throws Exception {
Session s = openSession();
FullTextSession session = Search.getFullTextSession( s );
Transaction tx = s.beginTransaction();
Actor bob = new Actor();
bob.id = 1L;
bob.name = "Bob";
s.persist( bob );
Actor bruce = new Actor();
bruce.id = 2L;
bruce.name = "Bruce";
s.persist( bruce );
Actor bert = new Actor();
bert.id = 3L;
bert.name = "Bert";
s.persist( bert );
Actor brent = new Actor();
brent.id = 4L;
brent.name = "Brent";
s.persist( brent );
tx.commit();
assertThat( errorHandler.getHandleInvocations() ).hasSize( 1 );
ErrorContext errorContext = errorHandler.getHandleInvocations().iterator().next();
assertThat( errorContext.getThrowable() ).isExactlyInstanceOf( BulkRequestFailedException.class );
assertThat( errorContext.getFailingOperations() ).onProperty( "idInString" ).containsOnly( "3" );
tx = s.beginTransaction();
QueryDescriptor query = ElasticsearchQueries.fromJson( "{ 'query': { 'match_all' : {} } }" );
List<?> result = session.createFullTextQuery( query, Actor.class ).list();
assertThat( result ).onProperty( "name" ).containsOnly( "Bob", "Bruce", "Brent" );
tx.commit();
s.close();
}
@Override
public void configure(Map<String, Object> settings) {
settings.put( Environment.ERROR_HANDLER, errorHandler );
}
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class[] { Actor.class };
}
public static class TestExceptionHandler implements ErrorHandler {
private List<ErrorContext> handleInvocations = new ArrayList<>();
@Override
public void handle(ErrorContext context) {
handleInvocations.add( context );
}
@Override
public void handleException(String errorMsg, Throwable exception) {
}
public List<ErrorContext> getHandleInvocations() {
return handleInvocations;
}
public void reset() {
handleInvocations.clear();
}
}
@Entity
@Indexed(index = "actor")
public static class Actor {
@Id
public Long id;
@Field(bridge = @FieldBridge(impl = ErroneousFieldBridge.class))
public String name;
public String getName() {
return name;
}
}
public static class ErroneousFieldBridge implements org.hibernate.search.bridge.FieldBridge {
@Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
String asString = (String) value;
luceneOptions.addFieldToDocument( name, asString, document );
if ( "Bert".equals( asString ) ) {
luceneOptions.addFieldToDocument( "unexpected", "unexpected", document );
}
}
}
}