Skip to content

Commit

Permalink
HSEARCH-1383 Don't accept interfaces and allow mapped superclasses
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta committed Apr 13, 2023
1 parent ce972ea commit 5149e59
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 11 deletions.
Expand Up @@ -69,7 +69,7 @@ public void partialSessionFilterFails() {
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"Use a different coordination strategy or an application-level automatic indexing filter.",
"Outbox polling coordination strategy allows only disabling indexing of all types at the session level.",
"The outbox polling coordination strategy allows only disabling the indexing of all types at the session level.",
"Applying a session-level automatic indexing filter that does not disable all types is prohibited."
);
} );
Expand All @@ -87,7 +87,7 @@ public void allTypesMixSessionFilterFails() {
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"Use a different coordination strategy or an application-level automatic indexing filter.",
"Outbox polling coordination strategy allows only disabling indexing of all types at the session level.",
"The outbox polling coordination strategy allows only disabling the indexing of all types at the session level.",
"Applying a session-level automatic indexing filter that does not disable all types is prohibited."
);
} );
Expand Down
Expand Up @@ -14,9 +14,11 @@
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;

import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded;
Expand Down Expand Up @@ -58,10 +60,12 @@ public void setup(OrmSetupHelper.SetupContext setupContext) {
.expectSchema( EntityA.INDEX, b -> b.field( "indexedField", String.class ) )
.expectSchema( Entity1A.INDEX, b -> b.field( "indexedField", String.class ) )
.expectSchema( Entity2A.INDEX, b -> b.field( "indexedField", String.class ) )
.expectSchema( Entity1B.INDEX, b -> b.field( "indexedField", String.class ) );
.expectSchema( Entity1B.INDEX, b -> b.field( "indexedField", String.class ) )
.expectSchema( EntityFromSuperclass.INDEX, b -> b.field( "indexedField", String.class ) );

setupContext.withAnnotatedTypes( IndexedEntity.class, OtherIndexedEntity.class, ContainedEntity.class,
EntityA.class, Entity1A.class, Entity1B.class, Entity2A.class
EntityA.class, Entity1A.class, Entity1B.class, Entity2A.class, EntityFromSuperclass.class, SuperClass.class,
SimpleNotIndexedEntity.class, NotIndexedEntityFromSuperclass.class
);
}

Expand Down Expand Up @@ -322,4 +326,111 @@ public Entity2A(Integer id, String indexedField) {
super( id, indexedField );
}
}


@MappedSuperclass
public static class SuperClass {
@Id
private Integer id;

@Basic
@GenericField
private String indexedField;

public SuperClass() {
}

public SuperClass(Integer id, String indexedField) {
this.id = id;
this.indexedField = indexedField;
}
}

public interface InterfaceA {
}

public interface InterfaceB {
}

@Entity(name = EntityFromSuperclass.INDEX)
@Indexed
public static class EntityFromSuperclass extends SuperClass implements InterfaceA, InterfaceB {
static final String INDEX = "EntityFromSuperclass";

public EntityFromSuperclass() {
}

public EntityFromSuperclass(Integer id, String indexedField) {
super( id, indexedField );
}
}

@Entity(name = NotIndexedEntityFromSuperclass.INDEX)
public static class NotIndexedEntityFromSuperclass extends SuperClass implements InterfaceA, InterfaceB {
static final String INDEX = "NotIndexedEntityFromSuperclass";

public NotIndexedEntityFromSuperclass() {
}

public NotIndexedEntityFromSuperclass(Integer id, String indexedField) {
super( id, indexedField );
}
}

@Entity
public static class SimpleNotIndexedEntity {
@Id
private Integer id;

@Basic
@GenericField
private String indexedField;

public SimpleNotIndexedEntity() {
}

public SimpleNotIndexedEntity(Integer id, String indexedField) {
this.id = id;
this.indexedField = indexedField;
}
}

public static class NotAnEntity {
}

@Indexed(index = IndexedNotAnEntity.INDEX)
public static class IndexedNotAnEntity {
static final String INDEX = "IndexedNotAnEntity";

@DocumentId
private Integer id;

@GenericField
private String indexedField;

public IndexedNotAnEntity() {
}

public IndexedNotAnEntity(Integer id, String indexedField) {
this.id = id;
this.indexedField = indexedField;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getIndexedField() {
return indexedField;
}

public void setIndexedField(String indexedField) {
this.indexedField = indexedField;
}
}

}
Expand Up @@ -319,4 +319,143 @@ public void applicationFilterExcludeSessionInclude() {
} );
backendMock.verifyExpectationsMet();
}

@Test
public void filterByMappedSuperclass() {
setupHolder.runInTransaction( session -> {
Search.session( session ).automaticIndexingFilter( ctx -> ctx.exclude( SuperClass.class ) );

session.persist( new EntityFromSuperclass( 100, "test" ) );
} );
backendMock.verifyExpectationsMet();
}

@Test
public void filterByNotIndexedEntity() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( SimpleNotIndexedEntity.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
SimpleNotIndexedEntity.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search"
);
} );
}

@Test
public void filterByRandomClass() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( NotAnEntity.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
NotAnEntity.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search"
);
} );
}

@Test
public void filterByNotIndexedEntityFormSupertypeWithIndexedSubtype() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( NotIndexedEntityFromSuperclass.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
NotIndexedEntityFromSuperclass.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search"
);
} );
}

@Test
public void filterByIndexedTypeNotAnEntity() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( IndexedNotAnEntity.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
IndexedNotAnEntity.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search"
);
} );
}

@Test
public void filterByContainedEntityWontAffectContainingOnes() {
setupHolder.runInTransaction( session -> {
// to prepare data we ignore containing/indexed entity
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( IndexedEntity.class )
);

IndexedEntity entity1 = new IndexedEntity();
entity1.setId( 1 );
entity1.setIndexedField( "initialValue" );

ContainedEntity entity2 = new ContainedEntity();
entity2.setId( 100 );
entity2.setIndexedField( "initialValue" );

entity2.setContainingAsIndexedEmbedded( entity1 );
entity1.setContainedIndexedEmbedded( Arrays.asList( entity2 ) );

session.persist( entity1 );
session.persist( entity2 );

} );
backendMock.verifyExpectationsMet();

setupHolder.runInTransaction( session -> {
// now disable contained entity to not produce updates on containing:
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( ContainedEntity.class )
);
ContainedEntity entity1 = session.get( ContainedEntity.class, 100 );
entity1.setIndexedField( "updatedValue" );
} );

setupHolder.runInTransaction( session -> {
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( IndexedEntity.class )
);
IndexedEntity entity1 = session.get( IndexedEntity.class, 1 );

entity1.getContainedIndexedEmbedded().forEach( e -> e.setContainingAsIndexedEmbedded( null ) );

session.remove( entity1 );

} );
backendMock.verifyExpectationsMet();
}

@Test
public void filterByInterfaceMustFail() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( InterfaceA.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
InterfaceA.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search.",
"Valid classes for mapped entity types are: "
);
} );
}
}
Expand Up @@ -13,7 +13,7 @@
import java.util.Set;

import org.hibernate.search.mapper.orm.logging.impl.Log;
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSessionTypeContextProvider;
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmAutomaticIndexingTypeFilterTypeContextProvider;
import org.hibernate.search.mapper.pojo.automaticindexing.filter.PojoAutomaticIndexingTypeFilterContext;
import org.hibernate.search.mapper.pojo.automaticindexing.filter.spi.PojoAutomaticIndexingTypeFilterHolder;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier;
Expand All @@ -24,12 +24,13 @@ public class HibernateOrmAutomaticIndexingTypeFilterContext implements PojoAutom

private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() );

private final HibernateOrmSessionTypeContextProvider contextProvider;
private final HibernateOrmAutomaticIndexingTypeFilterTypeContextProvider contextProvider;

private final Set<PojoRawTypeIdentifier<?>> includes = new HashSet<>();
private final Set<PojoRawTypeIdentifier<?>> excludes = new HashSet<>();

public HibernateOrmAutomaticIndexingTypeFilterContext(HibernateOrmSessionTypeContextProvider typeManager) {
public HibernateOrmAutomaticIndexingTypeFilterContext(
HibernateOrmAutomaticIndexingTypeFilterTypeContextProvider typeManager) {
this.contextProvider = typeManager;
}

Expand All @@ -46,7 +47,7 @@ public PojoAutomaticIndexingTypeFilterContext include(String name) {
@Override
public PojoAutomaticIndexingTypeFilterContext include(Class<?> clazz) {
addIfNotPresentInOther(
contextProvider.typeIdentifierResolver().resolveByJavaClass( clazz ),
contextProvider.indexedWithSuperTypesByExactClass().getOrFail( clazz ),
includes,
excludes
);
Expand All @@ -66,7 +67,7 @@ public PojoAutomaticIndexingTypeFilterContext exclude(String name) {
@Override
public PojoAutomaticIndexingTypeFilterContext exclude(Class<?> clazz) {
addIfNotPresentInOther(
contextProvider.typeIdentifierResolver().resolveByJavaClass( clazz ),
contextProvider.indexedWithSuperTypesByExactClass().getOrFail( clazz ),
excludes,
includes
);
Expand Down

0 comments on commit 5149e59

Please sign in to comment.