diff --git a/documentation/src/main/asciidoc/reference/coordination.asciidoc b/documentation/src/main/asciidoc/reference/coordination.asciidoc index d565ebe7bd5..d14b9cbb8e6 100644 --- a/documentation/src/main/asciidoc/reference/coordination.asciidoc +++ b/documentation/src/main/asciidoc/reference/coordination.asciidoc @@ -253,7 +253,7 @@ but it will remain asynchronous. [[coordination-outbox-polling-schema]] === [[coordination-database-polling-schema]] Impact on the database schema - +==== Basics The `outbox-polling` coordination strategy needs to store data in additional tables in the application database, so that this data can be consumed by background threads. @@ -277,6 +277,69 @@ in particular the Hibernate ORM properties `javax.persistence.schema-generation. `javax.persistence.schema-generation.scripts.create-target` and `javax.persistence.schema-generation.scripts.drop-target`. +==== Custom schema/table name/etc. +By default, outbox and agent tables, mentioned in the previous section, are expected to be found +in the default catalog/schema, and are using uppercased table names prefixed with `HSEARCH_`. +Identity generator names used for these tables are prefixed with `HSEARCH_` and suffixed with `_GENERATOR`. + +Sometimes there are specific naming conventions for database objects, or a need to separate the domain +and technical tables. +To allow some flexibility in this area, Hibernate Search provides a set of configuration properties +to specify catalog/schema/table/identity generator names for outbox event and agent tables: + +[source] +---- +# Configure the agent mapping: +hibernate.search.coordination.entity.mapping.agent.catalog=CUSTOM_CATALOG +hibernate.search.coordination.entity.mapping.agent.schema=CUSTOM_SCHEMA +hibernate.search.coordination.entity.mapping.agent.generator=CUSTOM_AGENT_GENERATOR +hibernate.search.coordination.entity.mapping.agent.table=CUSTOM_AGENT_TABLE +# Configure the outbox event mapping: +hibernate.search.coordination.entity.mapping.outboxevent.catalog=CUSTOM_CATALOG +hibernate.search.coordination.entity.mapping.outboxevent.schema=CUSTOM_SCHEMA +hibernate.search.coordination.entity.mapping.outboxevent.generator=CUSTOM_OUTBOX_GENERATOR +hibernate.search.coordination.entity.mapping.outboxevent.table=CUSTOM_OUTBOX_TABLE +---- + +* `agent.catalog` defines the database catalog to use for the agent table. ++ +Defaults to the default catalog configured in Hibernate ORM. +* `agent.schema` defines the database schema to use for the agent table. ++ +Defaults to the default schema configured in Hibernate ORM. +* `agent.table` defines the name of the agent table. ++ +Defaults to `HSEARCH_AGENT`. +* `agent.generator` defines the name of the identifier generator used for the agent table . ++ +Defaults to `HSEARCH_AGENT_GENERATOR`. + +* `outboxevent.catalog` defines the database catalog to use for the outbox event table. ++ +Defaults to the default catalog configured in Hibernate ORM. +* `outboxevent.schema` defines the database schema to use for the outbox event table. ++ +Defaults to the default schema configured in Hibernate ORM. +* `outboxevent.table` defines the name of the outbox events table. ++ +Defaults to `HSEARCH_OUTBOX_EVENT`. +* `outboxevent.generator` defines the name of the identifier generator used for the outbox event table. ++ +Defaults to `HSEARCH_OUTBOX_EVENT_GENERATOR`. + +[TIP] +==== +If your application relies on link:{hibernateDocUrl}#configurations-hbmddl[automatic database schema generation], +make sure that the underlying database supports catalogs/schemas +when specifying them. Also check if there are any constraints on name length and case sensitivity. +==== + +[TIP] +==== +It is not required to provide all properties at the same time. For example, you can customize the schema only. +Unspecified properties will use their defaults. +==== + [[coordination-outbox-polling-sharding]] === [[coordination-database-polling-sharding]] [[coordination-outbox-polling-sharding-basics]] Sharding and pulse diff --git a/integrationtest/mapper/orm-coordination-outbox-polling/src/test/java/org/hibernate/search/integrationtest/mapper/orm/coordination/outboxpolling/automaticindexing/OutboxPollingCustomEntityMappingIT.java b/integrationtest/mapper/orm-coordination-outbox-polling/src/test/java/org/hibernate/search/integrationtest/mapper/orm/coordination/outboxpolling/automaticindexing/OutboxPollingCustomEntityMappingIT.java index 8d8448fb41d..3006bfe73d6 100644 --- a/integrationtest/mapper/orm-coordination-outbox-polling/src/test/java/org/hibernate/search/integrationtest/mapper/orm/coordination/outboxpolling/automaticindexing/OutboxPollingCustomEntityMappingIT.java +++ b/integrationtest/mapper/orm-coordination-outbox-polling/src/test/java/org/hibernate/search/integrationtest/mapper/orm/coordination/outboxpolling/automaticindexing/OutboxPollingCustomEntityMappingIT.java @@ -8,8 +8,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hibernate.search.util.impl.integrationtest.mapper.orm.OrmUtils.withinTransaction; +import static org.junit.Assume.assumeTrue; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -21,28 +24,31 @@ import org.hibernate.boot.MappingException; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MySQLDialect; +import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.resource.jdbc.spi.StatementInspector; +import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.HibernateOrmMapperOutboxPollingSettings; import org.hibernate.search.mapper.orm.coordination.outboxpolling.cluster.impl.OutboxPollingAgentAdditionalJaxbMappingProducer; import org.hibernate.search.mapper.orm.coordination.outboxpolling.event.impl.OutboxPollingOutboxEventAdditionalJaxbMappingProducer; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; +import org.hibernate.search.util.common.SearchException; import org.hibernate.search.util.impl.integrationtest.common.rule.BackendMock; import org.hibernate.search.util.impl.integrationtest.mapper.orm.CoordinationStrategyExpectations; import org.hibernate.search.util.impl.integrationtest.mapper.orm.OrmSetupHelper; -import org.hibernate.search.util.impl.integrationtest.mapper.orm.OrmUtils; import org.junit.Rule; import org.junit.Test; public class OutboxPollingCustomEntityMappingIT { - private static final String ORIGINAL_OUTBOX_EVENT_TABLE_NAME = OutboxPollingOutboxEventAdditionalJaxbMappingProducer.TABLE_NAME; + private static final String CUSTOM_SCHEMA = "CUSTOM_SCHEMA"; + private static final String ORIGINAL_OUTBOX_EVENT_TABLE_NAME = HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_TABLE; private static final String CUSTOM_OUTBOX_EVENT_TABLE_NAME = "CUSTOM_OUTBOX_EVENT"; private static final String ORIGINAL_OUTBOX_EVENT_GENERATOR_NAME = ORIGINAL_OUTBOX_EVENT_TABLE_NAME + "_GENERATOR"; private static final String CUSTOM_OUTBOX_EVENT_GENERATOR_NAME = CUSTOM_OUTBOX_EVENT_TABLE_NAME + "_GENERATOR"; - private static final String ORIGINAL_AGENT_TABLE_NAME = OutboxPollingAgentAdditionalJaxbMappingProducer.TABLE_NAME; + private static final String ORIGINAL_AGENT_TABLE_NAME = HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_AGENT_TABLE; private static final String CUSTOM_AGENT_TABLE_NAME = "CUSTOM_AGENT"; private static final String ORIGINAL_AGENT_GENERATOR_NAME = ORIGINAL_AGENT_TABLE_NAME + "_GENERATOR"; private static final String CUSTOM_AGENT_GENERATOR_NAME = CUSTOM_AGENT_TABLE_NAME + "_GENERATOR"; @@ -65,7 +71,8 @@ public class OutboxPollingCustomEntityMappingIT { ORIGINAL_OUTBOX_EVENT_TABLE_NAME, CUSTOM_OUTBOX_EVENT_TABLE_NAME, ORIGINAL_OUTBOX_EVENT_GENERATOR_NAME, CUSTOM_OUTBOX_EVENT_GENERATOR_NAME, ORIGINAL_AGENT_TABLE_NAME, CUSTOM_AGENT_TABLE_NAME, - ORIGINAL_AGENT_GENERATOR_NAME, CUSTOM_AGENT_GENERATOR_NAME + ORIGINAL_AGENT_GENERATOR_NAME, CUSTOM_AGENT_GENERATOR_NAME, + CUSTOM_SCHEMA, }; } @@ -82,8 +89,7 @@ public class OutboxPollingCustomEntityMappingIT { public void wrongOutboxEventMapping() { assertThatThrownBy( () -> ormSetupHelper.start() .withProperty( "hibernate.search.coordination.outboxevent.entity.mapping", "" ) - .setup( IndexedEntity.class ) - ) + .setup( IndexedEntity.class ) ) .isInstanceOf( MappingException.class ) .hasMessageContainingAll( "Unable to perform unmarshalling", "unexpected element" ); } @@ -92,8 +98,7 @@ public void wrongOutboxEventMapping() { public void wrongAgentMapping() { assertThatThrownBy( () -> ormSetupHelper.start() .withProperty( "hibernate.search.coordination.agent.entity.mapping", "" ) - .setup( IndexedEntity.class ) - ) + .setup( IndexedEntity.class ) ) .isInstanceOf( MappingException.class ) .hasMessageContainingAll( "Unable to perform unmarshalling", "unexpected element" ); } @@ -109,15 +114,15 @@ public void validOutboxEventMapping() { .setup( IndexedEntity.class ); backendMock.verifyExpectationsMet(); - backendMock.expectWorks( IndexedEntity.INDEX ) - .add( "1", f -> f.field( "indexedField", "value for the field" ) ); - int id = 1; - OrmUtils.withinTransaction( sessionFactory, session -> { + withinTransaction( sessionFactory, session -> { IndexedEntity entity = new IndexedEntity(); entity.setId( id ); entity.setIndexedField( "value for the field" ); session.persist( entity ); + + backendMock.expectWorks( IndexedEntity.INDEX ) + .add( "1", f -> f.field( "indexedField", "value for the field" ) ); } ); backendMock.verifyExpectationsMet(); @@ -149,15 +154,15 @@ public void validAgentMapping() { .setup( IndexedEntity.class ); backendMock.verifyExpectationsMet(); - backendMock.expectWorks( IndexedEntity.INDEX ) - .add( "1", f -> f.field( "indexedField", "value for the field" ) ); - int id = 1; - OrmUtils.withinTransaction( sessionFactory, session -> { + withinTransaction( sessionFactory, session -> { IndexedEntity entity = new IndexedEntity(); entity.setId( id ); entity.setIndexedField( "value for the field" ); session.persist( entity ); + + backendMock.expectWorks( IndexedEntity.INDEX ) + .add( "1", f -> f.field( "indexedField", "value for the field" ) ); } ); backendMock.verifyExpectationsMet(); @@ -178,11 +183,131 @@ public void validAgentMapping() { assertThat( statementInspector.countByKey( CUSTOM_AGENT_GENERATOR_NAME ) ).isPositive(); } + @Test + public void conflictingAgentMappingConfiguration() { + assertThatThrownBy( () -> ormSetupHelper.start() + .withProperty( "hibernate.search.coordination.agent.entity.mapping", VALID_AGENT_EVENT_MAPPING ) + .withProperty( "hibernate.search.coordination.entity.mapping.agent.table", "break_it_all" ) + .setup( IndexedEntity.class ) ) + .isInstanceOf( SearchException.class ) + .hasMessageContaining( "Outbox polling agent configuration property conflict." ); + } + + @Test + public void conflictingOutboxeventMappingConfiguration() { + assertThatThrownBy( () -> ormSetupHelper.start() + .withProperty( "hibernate.search.coordination.outboxevent.entity.mapping", VALID_OUTBOX_EVENT_MAPPING ) + .withProperty( "hibernate.search.coordination.entity.mapping.outboxevent.table", "break_it_all" ) + .setup( IndexedEntity.class ) ) + .isInstanceOf( SearchException.class ) + .hasMessageContaining( "Outbox event configuration property conflict." ); + } + + @Test + public void validMappingWithCustomNames() { + KeysStatementInspector statementInspector = new KeysStatementInspector(); + + backendMock.expectAnySchema( IndexedEntity.INDEX ); + sessionFactory = ormSetupHelper.start() + .withProperty( "hibernate.search.coordination.entity.mapping.agent.generator", CUSTOM_AGENT_GENERATOR_NAME ) + .withProperty( "hibernate.search.coordination.entity.mapping.agent.table", CUSTOM_AGENT_TABLE_NAME ) + .withProperty( "hibernate.search.coordination.entity.mapping.outboxevent.generator", CUSTOM_OUTBOX_EVENT_GENERATOR_NAME ) + .withProperty( "hibernate.search.coordination.entity.mapping.outboxevent.table", CUSTOM_OUTBOX_EVENT_TABLE_NAME ) + .withProperty( "hibernate.session_factory.statement_inspector", statementInspector ) + .setup( IndexedEntity.class ); + backendMock.verifyExpectationsMet(); + + int id = 1; + withinTransaction( sessionFactory, session -> { + IndexedEntity entity = new IndexedEntity(); + entity.setId( id ); + entity.setIndexedField( "value for the field" ); + session.persist( entity ); + + backendMock.expectWorks( IndexedEntity.INDEX ) + .add( "1", f -> f.field( "indexedField", "value for the field" ) ); + } ); + backendMock.verifyExpectationsMet(); + + assertThat( statementInspector.countByKey( ORIGINAL_OUTBOX_EVENT_TABLE_NAME ) ).isZero(); + assertThat( statementInspector.countByKey( CUSTOM_OUTBOX_EVENT_TABLE_NAME ) ).isPositive(); + assertThat( statementInspector.countByKey( ORIGINAL_AGENT_TABLE_NAME ) ).isZero(); + assertThat( statementInspector.countByKey( CUSTOM_AGENT_TABLE_NAME ) ).isPositive(); + + if ( getDialect() instanceof MySQLDialect ) { + // statements for sequences are not reported to the interceptor with this dialect + return; + } + + assertThat( statementInspector.countByKey( ORIGINAL_OUTBOX_EVENT_GENERATOR_NAME ) ).isZero(); + assertThat( statementInspector.countByKey( CUSTOM_OUTBOX_EVENT_GENERATOR_NAME ) ).isPositive(); + assertThat( statementInspector.countByKey( ORIGINAL_AGENT_GENERATOR_NAME ) ).isZero(); + assertThat( statementInspector.countByKey( CUSTOM_AGENT_GENERATOR_NAME ) ).isPositive(); + } + + @Test + public void validMappingWithCustomNamesAndSchema() { + KeysStatementInspector statementInspector = new KeysStatementInspector(); + + backendMock.expectAnySchema( IndexedEntity.INDEX ); + sessionFactory = ormSetupHelper.start() + // Allow ORM to create schema as we want to use non-default for this testcase: + .withProperty( "javax.persistence.create-database-schemas", true ) + .withProperty( "hibernate.search.coordination.entity.mapping.agent.schema", CUSTOM_SCHEMA ) + .withProperty( "hibernate.search.coordination.entity.mapping.agent.generator", CUSTOM_AGENT_GENERATOR_NAME ) + .withProperty( "hibernate.search.coordination.entity.mapping.agent.table", CUSTOM_AGENT_TABLE_NAME ) + .withProperty( "hibernate.search.coordination.entity.mapping.outboxevent.schema", CUSTOM_SCHEMA ) + .withProperty( "hibernate.search.coordination.entity.mapping.outboxevent.generator", CUSTOM_OUTBOX_EVENT_GENERATOR_NAME ) + .withProperty( "hibernate.search.coordination.entity.mapping.outboxevent.table", CUSTOM_OUTBOX_EVENT_TABLE_NAME ) + .withProperty( "hibernate.session_factory.statement_inspector", statementInspector ) + .setup( IndexedEntity.class ); + backendMock.verifyExpectationsMet(); + + assumeTrue( "This test only makes sense if the database supports schemas", + getNameQualifierSupport().supportsSchemas() ); + assumeTrue( "This test only makes sense if the dialect supports creating schemas", + getDialect().canCreateSchema() ); + + int id = 1; + withinTransaction( sessionFactory, session -> { + IndexedEntity entity = new IndexedEntity(); + entity.setId( id ); + entity.setIndexedField( "value for the field" ); + session.persist( entity ); + + backendMock.expectWorks( IndexedEntity.INDEX ) + .add( "1", f -> f.field( "indexedField", "value for the field" ) ); + } ); + backendMock.verifyExpectationsMet(); + + assertThat( statementInspector.countByKey( ORIGINAL_OUTBOX_EVENT_TABLE_NAME ) ).isZero(); + assertThat( statementInspector.countByKey( CUSTOM_OUTBOX_EVENT_TABLE_NAME ) ).isPositive(); + assertThat( statementInspector.countByKey( ORIGINAL_AGENT_TABLE_NAME ) ).isZero(); + assertThat( statementInspector.countByKey( CUSTOM_AGENT_TABLE_NAME ) ).isPositive(); + + assertThat( statementInspector.countByKey( CUSTOM_SCHEMA ) ).isPositive(); + + if ( getDialect() instanceof MySQLDialect ) { + // statements for sequences are not reported to the interceptor with this dialect + return; + } + + assertThat( statementInspector.countByKey( ORIGINAL_OUTBOX_EVENT_GENERATOR_NAME ) ).isZero(); + assertThat( statementInspector.countByKey( CUSTOM_OUTBOX_EVENT_GENERATOR_NAME ) ).isPositive(); + assertThat( statementInspector.countByKey( ORIGINAL_AGENT_GENERATOR_NAME ) ).isZero(); + assertThat( statementInspector.countByKey( CUSTOM_AGENT_GENERATOR_NAME ) ).isPositive(); + } + private Dialect getDialect() { return sessionFactory.unwrap( SessionFactoryImplementor.class ).getJdbcServices() .getJdbcEnvironment().getDialect(); } + private NameQualifierSupport getNameQualifierSupport() { + return sessionFactory.unwrap( SessionFactoryImplementor.class ).getJdbcServices() + .getJdbcEnvironment().getNameQualifierSupport(); + } + @Entity(name = IndexedEntity.INDEX) @Indexed(index = IndexedEntity.INDEX) public static class IndexedEntity { @@ -233,7 +358,7 @@ public KeysStatementInspector() { @Override public String inspect(String sql) { for ( String key : SQL_KEYS ) { - if ( sql.contains( key ) ) { + if ( Arrays.stream( sql.split( "[^A-Za-z0-9_-]" ) ).anyMatch( token -> key.equals( token ) ) ) { sqlByKey.get( key ).add( sql ); } } diff --git a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cfg/HibernateOrmMapperOutboxPollingSettings.java b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cfg/HibernateOrmMapperOutboxPollingSettings.java index 1b740197b30..35b7692d92c 100644 --- a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cfg/HibernateOrmMapperOutboxPollingSettings.java +++ b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cfg/HibernateOrmMapperOutboxPollingSettings.java @@ -46,7 +46,7 @@ private HibernateOrmMapperOutboxPollingSettings() { /** * Whether the application will process entity change events. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Expects a Boolean value such as {@code true} or {@code false}, @@ -69,7 +69,7 @@ private HibernateOrmMapperOutboxPollingSettings() { * Failing that, some events may not be processed or may be processed twice or in the wrong order, * resulting in errors and/or out-of-sync indexes. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* When this property is set, {@value #COORDINATION_EVENT_PROCESSOR_SHARDS_ASSIGNED} must also be set. @@ -88,7 +88,7 @@ private HibernateOrmMapperOutboxPollingSettings() { * Failing that, some events may not be processed or may be processed twice or in the wrong order, * resulting in errors and/or out-of-sync indexes. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* When this property is set, {@value #COORDINATION_EVENT_PROCESSOR_SHARDS_TOTAL_COUNT} must also be set. @@ -107,7 +107,7 @@ private HibernateOrmMapperOutboxPollingSettings() { * In the event processor, how long to wait for another query to the outbox events table * after a query didn't return any event, in milliseconds. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Hibernate Search will wait that long before polling again if the last polling didn't return any event: @@ -130,7 +130,7 @@ private HibernateOrmMapperOutboxPollingSettings() { * How long, in milliseconds, the event processor can poll for events * before it must perform a "pulse". *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Every agent registers itself in a database table. @@ -173,7 +173,7 @@ private HibernateOrmMapperOutboxPollingSettings() { * How long, in milliseconds, an event processor "pulse" remains valid * before considering the agent disconnected and forcibly removing it from the cluster. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Every agent registers itself in a database table. @@ -209,7 +209,7 @@ private HibernateOrmMapperOutboxPollingSettings() { /** * In the event processor, how many outbox events, at most, are processed in a single transaction. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Expects a positive Integer value, such as {@code 50}, @@ -223,7 +223,7 @@ private HibernateOrmMapperOutboxPollingSettings() { /** * In the event processor, the timeout for transactions processing outbox events. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Only effective when a JTA transaction manager is configured. @@ -240,7 +240,7 @@ private HibernateOrmMapperOutboxPollingSettings() { /** * How long the event processor must wait before re-processing an event after its processing failed. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Expects a positive integer value in seconds, such as {@code 10}, @@ -257,7 +257,7 @@ private HibernateOrmMapperOutboxPollingSettings() { * In the mass indexer, how long to wait for another query to the agent table * when actively waiting for event processors to suspend themselves, in milliseconds. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Hibernate Search will wait that long before polling again when it finds other agents haven't suspended yet: @@ -279,7 +279,7 @@ private HibernateOrmMapperOutboxPollingSettings() { /** * How long, in milliseconds, the mass indexer can wait before it must perform a "pulse". *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Every agent registers itself in a database table. @@ -315,7 +315,7 @@ private HibernateOrmMapperOutboxPollingSettings() { * How long, in milliseconds, a mass indexer agent "pulse" remains valid * before considering the agent disconnected and forcibly removing it from the cluster. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value #COORDINATION_STRATEGY_NAME}. *

* Every agent registers itself in a database table. @@ -348,6 +348,95 @@ private HibernateOrmMapperOutboxPollingSettings() { public static final String COORDINATION_MASS_INDEXER_PULSE_EXPIRATION = PREFIX + Radicals.COORDINATION_MASS_INDEXER_PULSE_EXPIRATION; + + /** + * The database catalog to use for the outbox event table. + *

+ * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. + *

+ * Defaults to the default catalog configured in Hibernate ORM. See {@link org.hibernate.cfg.AvailableSettings#DEFAULT_CATALOG}. + */ + public static final String COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_CATALOG = + PREFIX + Radicals.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_CATALOG; + + /** + * The database schema to use for the outbox event table. + *

+ * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. + *

+ * Defaults to the default schema configured in Hibernate ORM. See {@link org.hibernate.cfg.AvailableSettings#DEFAULT_SCHEMA}. + */ + public static final String COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_SCHEMA = + PREFIX + Radicals.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_SCHEMA; + + /** + * The name of the outbox event table. + *

+ * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. + *

+ * The default for this value is {@value Defaults#COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_TABLE}. + */ + public static final String COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_TABLE = + PREFIX + Radicals.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_TABLE; + + /** + * The name of the identifier generator used for the outbox event table. + *

+ * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. + *

+ * The default for this value is {@value Defaults#COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_GENERATOR}. + */ + + public static final String COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_GENERATOR = + PREFIX + Radicals.COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_GENERATOR; + /** + * The database catalog to use for the agent table. + *

+ * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. + *

+ * Defaults to the default catalog configured in Hibernate ORM. See {@link org.hibernate.cfg.AvailableSettings#DEFAULT_CATALOG}. + */ + public static final String COORDINATION_ENTITY_MAPPING_AGENT_CATALOG = + PREFIX + Radicals.COORDINATION_ENTITY_MAPPING_AGENT_CATALOG; + + /** + * The database schema to use for the agent table. + *

+ * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. + *

+ * Defaults to the default schema configured in Hibernate ORM. See {@link org.hibernate.cfg.AvailableSettings#DEFAULT_SCHEMA}. + */ + public static final String COORDINATION_ENTITY_MAPPING_AGENT_SCHEMA = + PREFIX + Radicals.COORDINATION_ENTITY_MAPPING_AGENT_SCHEMA; + + /** + * The name of the agent table. + *

+ * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. + *

+ * The default for this value is {@value Defaults#COORDINATION_ENTITY_MAPPING_AGENT_TABLE}. + */ + public static final String COORDINATION_ENTITY_MAPPING_AGENT_TABLE = + PREFIX + Radicals.COORDINATION_ENTITY_MAPPING_AGENT_TABLE; + + /** + * The name of the identifier generator used for the agent table. + *

+ * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. + *

+ * The default for this value is {@value Defaults#COORDINATION_ENTITY_MAPPING_AGENT_GENERATOR}. + */ + public static final String COORDINATION_ENTITY_MAPPING_AGENT_GENERATOR = + PREFIX + Radicals.COORDINATION_ENTITY_MAPPING_AGENT_GENERATOR; + /** * Configuration property keys without the {@link #PREFIX prefix}. */ @@ -370,6 +459,14 @@ private Radicals() { public static final String COORDINATION_MASS_INDEXER_POLLING_INTERVAL = COORDINATION_PREFIX + CoordinationRadicals.MASS_INDEXER_POLLING_INTERVAL; public static final String COORDINATION_MASS_INDEXER_PULSE_INTERVAL = COORDINATION_PREFIX + CoordinationRadicals.MASS_INDEXER_PULSE_INTERVAL; public static final String COORDINATION_MASS_INDEXER_PULSE_EXPIRATION = COORDINATION_PREFIX + CoordinationRadicals.MASS_INDEXER_PULSE_EXPIRATION; + public static final String COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_CATALOG = COORDINATION_PREFIX + CoordinationRadicals.ENTITY_MAPPING_OUTBOXEVENT_CATALOG; + public static final String COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_SCHEMA = COORDINATION_PREFIX + CoordinationRadicals.ENTITY_MAPPING_OUTBOXEVENT_SCHEMA; + public static final String COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_TABLE = COORDINATION_PREFIX + CoordinationRadicals.ENTITY_MAPPING_OUTBOXEVENT_TABLE; + public static final String COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_GENERATOR = COORDINATION_PREFIX + CoordinationRadicals.ENTITY_MAPPING_OUTBOXEVENT_GENERATOR; + public static final String COORDINATION_ENTITY_MAPPING_AGENT_CATALOG = COORDINATION_PREFIX + CoordinationRadicals.ENTITY_MAPPING_AGENT_CATALOG; + public static final String COORDINATION_ENTITY_MAPPING_AGENT_SCHEMA = COORDINATION_PREFIX + CoordinationRadicals.ENTITY_MAPPING_AGENT_SCHEMA; + public static final String COORDINATION_ENTITY_MAPPING_AGENT_TABLE = COORDINATION_PREFIX + CoordinationRadicals.ENTITY_MAPPING_AGENT_TABLE; + public static final String COORDINATION_ENTITY_MAPPING_AGENT_GENERATOR = COORDINATION_PREFIX + CoordinationRadicals.ENTITY_MAPPING_AGENT_GENERATOR; } /** @@ -395,6 +492,18 @@ private CoordinationRadicals() { public static final String MASS_INDEXER_POLLING_INTERVAL = MASS_INDEXER_PREFIX + "polling_interval"; public static final String MASS_INDEXER_PULSE_INTERVAL = MASS_INDEXER_PREFIX + "pulse_interval"; public static final String MASS_INDEXER_PULSE_EXPIRATION = MASS_INDEXER_PREFIX + "pulse_expiration"; + + public static final String ENTITY_MAPPING_PREFIX = "entity.mapping."; + public static final String ENTITY_MAPPING_AGENT_PREFIX = ENTITY_MAPPING_PREFIX + "agent."; + public static final String ENTITY_MAPPING_AGENT_GENERATOR = ENTITY_MAPPING_AGENT_PREFIX + "generator"; + public static final String ENTITY_MAPPING_AGENT_TABLE = ENTITY_MAPPING_AGENT_PREFIX + "table"; + public static final String ENTITY_MAPPING_AGENT_SCHEMA = ENTITY_MAPPING_AGENT_PREFIX + "schema"; + public static final String ENTITY_MAPPING_AGENT_CATALOG = ENTITY_MAPPING_AGENT_PREFIX + "catalog"; + public static final String ENTITY_MAPPING_OUTBOXEVENT_PREFIX = ENTITY_MAPPING_PREFIX + "outboxevent."; + public static final String ENTITY_MAPPING_OUTBOXEVENT_GENERATOR = ENTITY_MAPPING_OUTBOXEVENT_PREFIX + "generator"; + public static final String ENTITY_MAPPING_OUTBOXEVENT_TABLE = ENTITY_MAPPING_OUTBOXEVENT_PREFIX + "table"; + public static final String ENTITY_MAPPING_OUTBOXEVENT_SCHEMA = ENTITY_MAPPING_OUTBOXEVENT_PREFIX + "schema"; + public static final String ENTITY_MAPPING_OUTBOXEVENT_CATALOG = ENTITY_MAPPING_OUTBOXEVENT_PREFIX + "catalog"; } /** @@ -414,6 +523,18 @@ private Defaults() { public static final int COORDINATION_MASS_INDEXER_POLLING_INTERVAL = 100; public static final int COORDINATION_MASS_INDEXER_PULSE_INTERVAL = 2000; public static final int COORDINATION_MASS_INDEXER_PULSE_EXPIRATION = 30000; + + // WARNING: Always use this prefix for all tables added by Hibernate Search: + // we guarantee that in the documentation. + public static final String HSEARCH_PREFIX = "HSEARCH_"; + + // Must not be longer than 20 characters, so that the generator does not exceed the 30 characters for Oracle11g + public static final String COORDINATION_ENTITY_MAPPING_AGENT_TABLE = HSEARCH_PREFIX + "AGENT"; + public static final String COORDINATION_ENTITY_MAPPING_AGENT_GENERATOR = COORDINATION_ENTITY_MAPPING_AGENT_TABLE + "_GENERATOR"; + + // Must not be longer than 20 characters, so that the generator does not exceed the 30 characters for Oracle11g + public static final String COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_TABLE = HSEARCH_PREFIX + "OUTBOX_EVENT"; + public static final String COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_GENERATOR = COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_TABLE + "_GENERATOR"; } /** diff --git a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cfg/spi/HibernateOrmMapperOutboxPollingSpiSettings.java b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cfg/spi/HibernateOrmMapperOutboxPollingSpiSettings.java index 6696c093085..def7db7573e 100644 --- a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cfg/spi/HibernateOrmMapperOutboxPollingSpiSettings.java +++ b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cfg/spi/HibernateOrmMapperOutboxPollingSpiSettings.java @@ -8,9 +8,7 @@ import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings; import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.HibernateOrmMapperOutboxPollingSettings; -import org.hibernate.search.mapper.orm.coordination.outboxpolling.cluster.impl.Agent; import org.hibernate.search.mapper.orm.coordination.outboxpolling.cluster.impl.OutboxPollingAgentAdditionalJaxbMappingProducer; -import org.hibernate.search.mapper.orm.coordination.outboxpolling.event.impl.OutboxEvent; import org.hibernate.search.mapper.orm.coordination.outboxpolling.event.impl.OutboxPollingOutboxEventAdditionalJaxbMappingProducer; import org.hibernate.search.util.common.annotation.Incubating; @@ -30,26 +28,42 @@ private HibernateOrmMapperOutboxPollingSpiSettings() { public static final String PREFIX = HibernateOrmMapperSettings.PREFIX; /** - * Allows the user to define a specific Hibernate mapping for the {@link OutboxEvent} entity. + * Allows the user to define a specific Hibernate mapping for the outbox event table. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. *

* Expects a String value containing the xml expressing the Hibernate mapping for the entity. *

* The default for this value is {@link OutboxPollingOutboxEventAdditionalJaxbMappingProducer#ENTITY_DEFINITION} + *

+ * As this configuration entirely overrides the entity mapping it cannot be used in combination with any properties + * that define names of catalog/schema/table/identity generator for the outbox event table ( + * {@link HibernateOrmMapperOutboxPollingSettings#COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_CATALOG}, + * {@link HibernateOrmMapperOutboxPollingSettings#COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_SCHEMA}, + * {@link HibernateOrmMapperOutboxPollingSettings#COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_TABLE}, + * {@link HibernateOrmMapperOutboxPollingSettings#COORDINATION_ENTITY_MAPPING_OUTBOXEVENT_GENERATOR}). + * An exception ({@link org.hibernate.search.util.common.SearchException} will be thrown in case of such misconfiguration. */ public static final String OUTBOXEVENT_ENTITY_MAPPING = PREFIX + Radicals.OUTBOXEVENT_ENTITY_MAPPING; /** - * Allows the user to define a specific Hibernate mapping for the {@link Agent} entity. + * Allows the user to define a specific Hibernate mapping for the agent table. *

- * Only available when {@link HibernateOrmMapperSettings#COORDINATION_STRATEGY} is + * Only available when {@value HibernateOrmMapperSettings#COORDINATION_STRATEGY} is * {@value HibernateOrmMapperOutboxPollingSettings#COORDINATION_STRATEGY_NAME}. *

* Expects a String value containing the xml expressing the Hibernate mapping for the entity. *

* The default for this value is {@link OutboxPollingAgentAdditionalJaxbMappingProducer#ENTITY_DEFINITION} + *

+ * As this configuration entirely overrides the entity mapping it cannot be used in combination with any properties + * that define names of catalog/schema/table/identity generator for the agent table ( + * {@link HibernateOrmMapperOutboxPollingSettings#COORDINATION_ENTITY_MAPPING_AGENT_CATALOG}, + * {@link HibernateOrmMapperOutboxPollingSettings#COORDINATION_ENTITY_MAPPING_AGENT_SCHEMA}, + * {@link HibernateOrmMapperOutboxPollingSettings#COORDINATION_ENTITY_MAPPING_AGENT_TABLE}, + * {@link HibernateOrmMapperOutboxPollingSettings#COORDINATION_ENTITY_MAPPING_AGENT_GENERATOR}). + * An exception ({@link org.hibernate.search.util.common.SearchException} will be thrown in case of such misconfiguration. */ public static final String AGENT_ENTITY_MAPPING = PREFIX + Radicals.AGENT_ENTITY_MAPPING; @@ -62,8 +76,10 @@ private Radicals() { } public static final String COORDINATION_PREFIX = HibernateOrmMapperSettings.Radicals.COORDINATION_PREFIX; + public static final String OUTBOXEVENT_ENTITY_MAPPING = COORDINATION_PREFIX + CoordinationRadicals.OUTBOXEVENT_ENTITY_MAPPING; public static final String AGENT_ENTITY_MAPPING = COORDINATION_PREFIX + CoordinationRadicals.AGENT_ENTITY_MAPPING; + } public static final class CoordinationRadicals { @@ -73,6 +89,7 @@ private CoordinationRadicals() { public static final String OUTBOXEVENT_ENTITY_MAPPING = "outboxevent.entity.mapping"; public static final String AGENT_ENTITY_MAPPING = "agent.entity.mapping"; + } } diff --git a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cluster/impl/OutboxPollingAgentAdditionalJaxbMappingProducer.java b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cluster/impl/OutboxPollingAgentAdditionalJaxbMappingProducer.java index 37a19e2b978..84efb07e7dd 100644 --- a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cluster/impl/OutboxPollingAgentAdditionalJaxbMappingProducer.java +++ b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cluster/impl/OutboxPollingAgentAdditionalJaxbMappingProducer.java @@ -11,6 +11,8 @@ import java.lang.invoke.MethodHandles; import java.util.Collection; import java.util.Collections; +import java.util.Locale; +import java.util.Optional; import org.hibernate.boot.jaxb.Origin; import org.hibernate.boot.jaxb.SourceType; @@ -21,7 +23,9 @@ import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.search.engine.cfg.ConfigurationPropertySource; import org.hibernate.search.engine.cfg.spi.ConfigurationProperty; +import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty; import org.hibernate.search.mapper.orm.bootstrap.spi.HibernateSearchOrmMappingProducer; +import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.HibernateOrmMapperOutboxPollingSettings; import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.spi.HibernateOrmMapperOutboxPollingSpiSettings; import org.hibernate.search.mapper.orm.coordination.outboxpolling.logging.impl.Log; import org.hibernate.search.util.common.annotation.impl.SuppressForbiddenApis; @@ -32,13 +36,6 @@ public class OutboxPollingAgentAdditionalJaxbMappingProducer private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - // WARNING: Always use this prefix for all tables added by Hibernate Search: - // we guarantee that in the documentation. - public static final String HSEARCH_PREFIX = "HSEARCH_"; - - // Must not be longer than 20 characters, so that the generator does not exceed the 30 characters for Oracle11g - public static final String TABLE_NAME = HSEARCH_PREFIX + "AGENT"; - private static final String CLASS_NAME = Agent.class.getName(); // Setting both the JPA entity name and the native entity name to the FQCN so that: @@ -48,13 +45,13 @@ public class OutboxPollingAgentAdditionalJaxbMappingProducer // because our override actually matches the default for the native entity name. public static final String ENTITY_NAME = CLASS_NAME; - public static final String ENTITY_DEFINITION = "\n" + - "\n" + - " \n" + + private static final String ENTITY_DEFINITION_TEMPLATE = "\n" + + "\n" + + " \n" + " \n" + " \n" + - " " + TABLE_NAME + "_GENERATOR\n" + - " " + TABLE_NAME + "_GENERATOR\n" + + " %4$s\n" + + " %4$s\n" + " 1\n" + " 1\n" + " \n" + @@ -78,10 +75,40 @@ public class OutboxPollingAgentAdditionalJaxbMappingProducer " \n" + "\n"; - private static final ConfigurationProperty AGENT_ENTITY_MAPPING = - ConfigurationProperty.forKey( HibernateOrmMapperOutboxPollingSpiSettings.CoordinationRadicals.AGENT_ENTITY_MAPPING ) + public static final String ENTITY_DEFINITION = String.format( + Locale.ROOT, ENTITY_DEFINITION_TEMPLATE, "", "", + HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_AGENT_TABLE, + HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_AGENT_GENERATOR + ); + + private static final OptionalConfigurationProperty AGENT_ENTITY_MAPPING = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSpiSettings.CoordinationRadicals.AGENT_ENTITY_MAPPING ) + .asString() + .build(); + + private static final OptionalConfigurationProperty ENTITY_MAPPING_AGENT_SCHEMA = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSettings.CoordinationRadicals.ENTITY_MAPPING_AGENT_SCHEMA ) + .asString() + .build(); + + private static final OptionalConfigurationProperty ENTITY_MAPPING_AGENT_CATALOG = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSettings.CoordinationRadicals.ENTITY_MAPPING_AGENT_CATALOG ) + .asString() + .build(); + + private static final OptionalConfigurationProperty ENTITY_MAPPING_AGENT_TABLE = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSettings.CoordinationRadicals.ENTITY_MAPPING_AGENT_TABLE ) + .asString() + .build(); + + private static final OptionalConfigurationProperty ENTITY_MAPPING_AGENT_GENERATOR = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSettings.CoordinationRadicals.ENTITY_MAPPING_AGENT_GENERATOR ) .asString() - .withDefault( ENTITY_DEFINITION ) .build(); @Override @@ -89,7 +116,37 @@ public class OutboxPollingAgentAdditionalJaxbMappingProducer + " and there's nothing we can do about it") public Collection produceMappings(ConfigurationPropertySource propertySource, MappingBinder mappingBinder, MetadataBuildingContext buildingContext) { - String entityDefinition = AGENT_ENTITY_MAPPING.get( propertySource ); + + Optional mapping = AGENT_ENTITY_MAPPING.get( propertySource ); + Optional schema = ENTITY_MAPPING_AGENT_SCHEMA.get( propertySource ); + Optional catalog = ENTITY_MAPPING_AGENT_CATALOG.get( propertySource ); + Optional table = ENTITY_MAPPING_AGENT_TABLE.get( propertySource ); + Optional generator = ENTITY_MAPPING_AGENT_GENERATOR.get( propertySource ); + + // only allow configuring the entire mapping or table/catalog/schema/generator names + if ( mapping.isPresent() && ( schema.isPresent() || catalog.isPresent() || table.isPresent() || generator.isPresent() ) ) { + throw log.agentConfigurationPropertyConflict( + AGENT_ENTITY_MAPPING.resolveOrRaw( propertySource ), + new String[] { + ENTITY_MAPPING_AGENT_SCHEMA.resolveOrRaw( propertySource ), + ENTITY_MAPPING_AGENT_CATALOG.resolveOrRaw( propertySource ), + ENTITY_MAPPING_AGENT_TABLE.resolveOrRaw( propertySource ), + ENTITY_MAPPING_AGENT_GENERATOR.resolveOrRaw( propertySource ) + } + ); + } + + String entityDefinition = mapping.orElseGet( () -> + String.format( + Locale.ROOT, + ENTITY_DEFINITION_TEMPLATE, + schema.orElse( "" ), + catalog.orElse( "" ), + table.orElse( HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_AGENT_TABLE ), + generator.orElse( HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_AGENT_GENERATOR ) + + ) + ); log.agentGeneratedEntityMapping( entityDefinition ); Origin origin = new Origin( SourceType.OTHER, "search" ); diff --git a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/event/impl/OutboxPollingOutboxEventAdditionalJaxbMappingProducer.java b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/event/impl/OutboxPollingOutboxEventAdditionalJaxbMappingProducer.java index 085409d2b84..6a288183149 100644 --- a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/event/impl/OutboxPollingOutboxEventAdditionalJaxbMappingProducer.java +++ b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/event/impl/OutboxPollingOutboxEventAdditionalJaxbMappingProducer.java @@ -11,6 +11,8 @@ import java.lang.invoke.MethodHandles; import java.util.Collection; import java.util.Collections; +import java.util.Locale; +import java.util.Optional; import org.hibernate.boot.jaxb.Origin; import org.hibernate.boot.jaxb.SourceType; @@ -21,9 +23,10 @@ import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.search.engine.cfg.ConfigurationPropertySource; import org.hibernate.search.engine.cfg.spi.ConfigurationProperty; +import org.hibernate.search.engine.cfg.spi.OptionalConfigurationProperty; import org.hibernate.search.mapper.orm.bootstrap.spi.HibernateSearchOrmMappingProducer; +import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.HibernateOrmMapperOutboxPollingSettings; import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.spi.HibernateOrmMapperOutboxPollingSpiSettings; -import org.hibernate.search.mapper.orm.coordination.outboxpolling.cluster.impl.OutboxPollingAgentAdditionalJaxbMappingProducer; import org.hibernate.search.mapper.orm.coordination.outboxpolling.logging.impl.Log; import org.hibernate.search.util.common.annotation.impl.SuppressForbiddenApis; import org.hibernate.search.util.common.logging.impl.LoggerFactory; @@ -33,11 +36,6 @@ public final class OutboxPollingOutboxEventAdditionalJaxbMappingProducer private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() ); - private static final String HSEARCH_PREFIX = OutboxPollingAgentAdditionalJaxbMappingProducer.HSEARCH_PREFIX; - - // Must not be longer than 20 characters, so that the generator does not exceed the 30 characters for Oracle11g - public static final String TABLE_NAME = HSEARCH_PREFIX + "OUTBOX_EVENT"; - private static final String CLASS_NAME = OutboxEvent.class.getName(); // Setting both the JPA entity name and the native entity name to the FQCN so that: @@ -47,13 +45,13 @@ public final class OutboxPollingOutboxEventAdditionalJaxbMappingProducer // because our override actually matches the default for the native entity name. public static final String ENTITY_NAME = CLASS_NAME; - public static final String ENTITY_DEFINITION = "\n" + - "\n" + - " \n" + + public static final String ENTITY_DEFINITION_TEMPLATE = "\n" + + "\n" + + " \n" + " \n" + " \n" + - " " + TABLE_NAME + "_GENERATOR\n" + - " " + TABLE_NAME + "_GENERATOR\n" + + " %4$s\n" + + " %4$s\n" + " 1\n" + " 1\n" + " \n" + @@ -72,10 +70,40 @@ public final class OutboxPollingOutboxEventAdditionalJaxbMappingProducer " \n" + "\n"; - private static final ConfigurationProperty OUTBOXEVENT_ENTITY_MAPPING = - ConfigurationProperty.forKey( HibernateOrmMapperOutboxPollingSpiSettings.CoordinationRadicals.OUTBOXEVENT_ENTITY_MAPPING ) + public static final String ENTITY_DEFINITION = String.format( + Locale.ROOT, ENTITY_DEFINITION_TEMPLATE, "", "", + HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_TABLE, + HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_GENERATOR + ); + + private static final OptionalConfigurationProperty OUTBOXEVENT_ENTITY_MAPPING = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSpiSettings.CoordinationRadicals.OUTBOXEVENT_ENTITY_MAPPING ) + .asString() + .build(); + + private static final OptionalConfigurationProperty ENTITY_MAPPING_OUTBOXEVENT_SCHEMA = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSettings.CoordinationRadicals.ENTITY_MAPPING_OUTBOXEVENT_SCHEMA ) + .asString() + .build(); + + private static final OptionalConfigurationProperty ENTITY_MAPPING_OUTBOXEVENT_CATALOG = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSettings.CoordinationRadicals.ENTITY_MAPPING_OUTBOXEVENT_CATALOG ) + .asString() + .build(); + + private static final OptionalConfigurationProperty ENTITY_MAPPING_OUTBOXEVENT_TABLE = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSettings.CoordinationRadicals.ENTITY_MAPPING_OUTBOXEVENT_TABLE ) + .asString() + .build(); + + private static final OptionalConfigurationProperty ENTITY_MAPPING_OUTBOXEVENT_GENERATOR = + ConfigurationProperty.forKey( + HibernateOrmMapperOutboxPollingSettings.CoordinationRadicals.ENTITY_MAPPING_OUTBOXEVENT_GENERATOR ) .asString() - .withDefault( ENTITY_DEFINITION ) .build(); @Override @@ -83,7 +111,37 @@ public final class OutboxPollingOutboxEventAdditionalJaxbMappingProducer + " and there's nothing we can do about it") public Collection produceMappings(ConfigurationPropertySource propertySource, MappingBinder mappingBinder, MetadataBuildingContext buildingContext) { - String entityDefinition = OUTBOXEVENT_ENTITY_MAPPING.get( propertySource ); + + Optional mapping = OUTBOXEVENT_ENTITY_MAPPING.get( propertySource ); + Optional schema = ENTITY_MAPPING_OUTBOXEVENT_SCHEMA.get( propertySource ); + Optional catalog = ENTITY_MAPPING_OUTBOXEVENT_CATALOG.get( propertySource ); + Optional table = ENTITY_MAPPING_OUTBOXEVENT_TABLE.get( propertySource ); + Optional generator = ENTITY_MAPPING_OUTBOXEVENT_GENERATOR.get( propertySource ); + + // only allow configuring the entire mapping or table/catalog/schema/generator names + if ( mapping.isPresent() && ( schema.isPresent() || catalog.isPresent() || table.isPresent() || generator.isPresent() ) ) { + throw log.outboxEventConfigurationPropertyConflict( + OUTBOXEVENT_ENTITY_MAPPING.resolveOrRaw( propertySource ), + new String[] { + ENTITY_MAPPING_OUTBOXEVENT_SCHEMA.resolveOrRaw( propertySource ), + ENTITY_MAPPING_OUTBOXEVENT_CATALOG.resolveOrRaw( propertySource ), + ENTITY_MAPPING_OUTBOXEVENT_TABLE.resolveOrRaw( propertySource ), + ENTITY_MAPPING_OUTBOXEVENT_GENERATOR.resolveOrRaw( propertySource ) + } + ); + } + + String entityDefinition = mapping.orElseGet( () -> + String.format( + Locale.ROOT, + ENTITY_DEFINITION_TEMPLATE, + schema.orElse( "" ), + catalog.orElse( "" ), + table.orElse( HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_TABLE ), + generator.orElse( HibernateOrmMapperOutboxPollingSettings.Defaults.COORDINATION_ENTITY_MAPPING_OUTBOX_EVENT_GENERATOR ) + + ) + ); log.outboxEventGeneratedEntityMapping( entityDefinition ); Origin origin = new Origin( SourceType.OTHER, "search" ); diff --git a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/logging/impl/Log.java b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/logging/impl/Log.java index c733b5b740f..294540f6baa 100644 --- a/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/logging/impl/Log.java +++ b/mapper/orm-coordination-outbox-polling/src/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/logging/impl/Log.java @@ -160,4 +160,12 @@ SearchException conflictingOutboxEventBackgroundProcessorAgentShardsForStaticSha value = "Multi-tenancy is not enabled but a tenant id is specified. Trying to use the tenant id: '%1$s'.") SearchException multiTenancyNotEnabled(String tenantId); + @Message(id = ID_OFFSET + 26, value = "Outbox polling agent configuration property conflict." + + " Either mapping property %1$s or subset of name adjustment properties %2$s should be provided at the same time.") + SearchException agentConfigurationPropertyConflict(String mappingPropertyName, String[] nameAdjustmentProperties); + + @Message(id = ID_OFFSET + 27, value = "Outbox event configuration property conflict." + + " Either mapping property %1$s or subset of name adjustment properties %2$s should be provided at the same time.") + SearchException outboxEventConfigurationPropertyConflict(String mappingPropertyName, String[] nameAdjustmentProperties); + } diff --git a/orm6/integrationtest/mapper/orm/ant-src-changes.patch b/orm6/integrationtest/mapper/orm/ant-src-changes.patch index b3b8cb35336..967f5c1adcd 100644 --- a/orm6/integrationtest/mapper/orm/ant-src-changes.patch +++ b/orm6/integrationtest/mapper/orm/ant-src-changes.patch @@ -903,7 +903,7 @@ index 798418f3a9..bbfc99e232 100644 .contains( "_containingIndexBackref" ); diff --git a/test/java/org/hibernate/search/integrationtest/mapper/orm/model/GenericPropertyIT.java b/test/java/org/hibernate/search/integrationtest/mapper/orm/model/GenericPropertyIT.java -index d110dbb171..0796b95350 100644 +index aa24c725e1..66c2063aea 100644 --- a/test/java/org/hibernate/search/integrationtest/mapper/orm/model/GenericPropertyIT.java +++ b/test/java/org/hibernate/search/integrationtest/mapper/orm/model/GenericPropertyIT.java @@ -6,6 +6,7 @@ @@ -914,7 +914,7 @@ index d110dbb171..0796b95350 100644 import java.util.ArrayList; import java.util.List; import jakarta.persistence.Basic; -@@ -121,25 +122,17 @@ public void setGenericProperty(GenericEntity genericProperty) { +@@ -117,25 +118,17 @@ public void setGenericProperty(GenericEntity genericProperty) { } @Entity(name = "generic") diff --git a/orm6/mapper/orm-coordination-outbox-polling/ant-src-changes.patch b/orm6/mapper/orm-coordination-outbox-polling/ant-src-changes.patch index d98554e3b09..b6b41739cea 100644 --- a/orm6/mapper/orm-coordination-outbox-polling/ant-src-changes.patch +++ b/orm6/mapper/orm-coordination-outbox-polling/ant-src-changes.patch @@ -1,17 +1,17 @@ diff --git a/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cluster/impl/OutboxPollingAgentAdditionalJaxbMappingProducer.java b/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cluster/impl/OutboxPollingAgentAdditionalJaxbMappingProducer.java -index 37a19e2b97..c2255a15ae 100644 +index 84efb07e7d..33447ee586 100644 --- a/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cluster/impl/OutboxPollingAgentAdditionalJaxbMappingProducer.java +++ b/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/cluster/impl/OutboxPollingAgentAdditionalJaxbMappingProducer.java -@@ -32,6 +32,8 @@ +@@ -36,6 +36,8 @@ private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() ); + public static final String HIBERNATE_SEARCH = "hibernate-search"; + - // WARNING: Always use this prefix for all tables added by Hibernate Search: - // we guarantee that in the documentation. - public static final String HSEARCH_PREFIX = "HSEARCH_"; -@@ -100,7 +102,7 @@ public Collection produceMappings(ConfigurationPropertySource p + private static final String CLASS_NAME = Agent.class.getName(); + + // Setting both the JPA entity name and the native entity name to the FQCN so that: +@@ -157,7 +159,7 @@ public Collection produceMappings(ConfigurationPropertySource p JaxbHbmHibernateMapping root = (JaxbHbmHibernateMapping) binding.getRoot(); @@ -21,19 +21,27 @@ index 37a19e2b97..c2255a15ae 100644 } } diff --git a/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/event/impl/OutboxPollingOutboxEventAdditionalJaxbMappingProducer.java b/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/event/impl/OutboxPollingOutboxEventAdditionalJaxbMappingProducer.java -index 085409d2b8..3f497919bb 100644 +index 6a28818314..0060583360 100644 --- a/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/event/impl/OutboxPollingOutboxEventAdditionalJaxbMappingProducer.java +++ b/main/java/org/hibernate/search/mapper/orm/coordination/outboxpolling/event/impl/OutboxPollingOutboxEventAdditionalJaxbMappingProducer.java -@@ -33,6 +33,8 @@ +@@ -27,6 +27,7 @@ + import org.hibernate.search.mapper.orm.bootstrap.spi.HibernateSearchOrmMappingProducer; + import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.HibernateOrmMapperOutboxPollingSettings; + import org.hibernate.search.mapper.orm.coordination.outboxpolling.cfg.spi.HibernateOrmMapperOutboxPollingSpiSettings; ++import org.hibernate.search.mapper.orm.coordination.outboxpolling.cluster.impl.OutboxPollingAgentAdditionalJaxbMappingProducer; + import org.hibernate.search.mapper.orm.coordination.outboxpolling.logging.impl.Log; + import org.hibernate.search.util.common.annotation.impl.SuppressForbiddenApis; + import org.hibernate.search.util.common.logging.impl.LoggerFactory; +@@ -36,6 +37,8 @@ private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() ); + private static final String HIBERNATE_SEARCH = OutboxPollingAgentAdditionalJaxbMappingProducer.HIBERNATE_SEARCH; + - private static final String HSEARCH_PREFIX = OutboxPollingAgentAdditionalJaxbMappingProducer.HSEARCH_PREFIX; + private static final String CLASS_NAME = OutboxEvent.class.getName(); - // Must not be longer than 20 characters, so that the generator does not exceed the 30 characters for Oracle11g -@@ -63,7 +65,7 @@ + // Setting both the JPA entity name and the native entity name to the FQCN so that: +@@ -61,7 +64,7 @@ " \n" + " \n" + " \n" + @@ -42,8 +50,8 @@ index 085409d2b8..3f497919bb 100644 " \n" + " \n" + " " + OutboxEvent.Status.class.getName() + "\n" + -@@ -86,7 +88,7 @@ public Collection produceMappings(ConfigurationPropertySource p - String entityDefinition = OUTBOXEVENT_ENTITY_MAPPING.get( propertySource ); +@@ -144,7 +147,7 @@ public Collection produceMappings(ConfigurationPropertySource p + ); log.outboxEventGeneratedEntityMapping( entityDefinition ); - Origin origin = new Origin( SourceType.OTHER, "search" ); @@ -51,7 +59,7 @@ index 085409d2b8..3f497919bb 100644 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream( entityDefinition.getBytes() ); BufferedInputStream bufferedInputStream = new BufferedInputStream( byteArrayInputStream ); -@@ -94,7 +96,7 @@ public Collection produceMappings(ConfigurationPropertySource p +@@ -152,7 +155,7 @@ public Collection produceMappings(ConfigurationPropertySource p JaxbHbmHibernateMapping root = (JaxbHbmHibernateMapping) binding.getRoot();