mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-10-03 15:59:31 +00:00
Replace PropertyBasedRequiredBackgroundBootstrapping with exception
Replace `PropertyBasedRequiredBackgroundBootstrapping` with a simpler `BootstrapExecutorRequiredException` and use a regular failure analyzer to provide help. See gh-49733 See gh-49688
This commit is contained in:
+3
-3
@@ -33,8 +33,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.data.jpa.autoconfigure.DataJpaRepositoriesAutoConfiguration.JpaRepositoriesImportSelector;
|
||||
import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.jpa.autoconfigure.BootstrapExecutorRequiredException;
|
||||
import org.springframework.boot.jpa.autoconfigure.EntityManagerFactoryBuilderCustomizer;
|
||||
import org.springframework.boot.jpa.autoconfigure.PropertyBasedRequiredBackgroundBootstrapping;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
@@ -85,8 +85,8 @@ public final class DataJpaRepositoriesAutoConfiguration {
|
||||
@ConditionalOnProperty(name = "spring.data.jpa.repositories.bootstrap-mode", havingValue = "deferred")
|
||||
EntityManagerFactoryBuilderCustomizer entityManagerFactoryBootstrapExecutorCustomizer(
|
||||
Map<String, AsyncTaskExecutor> taskExecutors) {
|
||||
return (builder) -> builder.requireBootstrapExecutor(new PropertyBasedRequiredBackgroundBootstrapping(
|
||||
"spring.data.jpa.repositories.bootstrap-mode", "deferred"));
|
||||
return (builder) -> builder.requireBootstrapExecutor(() -> BootstrapExecutorRequiredException
|
||||
.ofProperty("spring.data.jpa.repositories.bootstrap-mode", "deferred"));
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
+3
-3
@@ -68,7 +68,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.diagnostics.FailureAnalyzedException;
|
||||
import org.springframework.boot.flyway.autoconfigure.FlywayAutoConfiguration;
|
||||
import org.springframework.boot.hibernate.SpringImplicitNamingStrategy;
|
||||
import org.springframework.boot.hibernate.autoconfigure.HibernateJpaAutoConfigurationTests.JpaUsingApplicationListenerConfiguration.EventCapturingApplicationListener;
|
||||
@@ -82,6 +81,7 @@ import org.springframework.boot.jdbc.autoconfigure.DataSourceInitializationAutoC
|
||||
import org.springframework.boot.jdbc.autoconfigure.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.jdbc.autoconfigure.XADataSourceAutoConfiguration;
|
||||
import org.springframework.boot.jpa.EntityManagerFactoryBuilder;
|
||||
import org.springframework.boot.jpa.autoconfigure.BootstrapExecutorRequiredException;
|
||||
import org.springframework.boot.jpa.autoconfigure.EntityManagerFactoryBuilderCustomizer;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaBaseConfiguration;
|
||||
import org.springframework.boot.jpa.autoconfigure.JpaProperties;
|
||||
@@ -274,7 +274,7 @@ class HibernateJpaAutoConfigurationTests {
|
||||
this.contextRunner.withPropertyValues("spring.jpa.bootstrap=async")
|
||||
.run((context) -> assertThat(context).getFailure()
|
||||
.rootCause()
|
||||
.isInstanceOf(FailureAnalyzedException.class)
|
||||
.isInstanceOf(BootstrapExecutorRequiredException.class)
|
||||
.message()
|
||||
.contains("bootstrap executor is required when 'spring.jpa.bootstrap' is set to 'async'"));
|
||||
}
|
||||
@@ -285,7 +285,7 @@ class HibernateJpaAutoConfigurationTests {
|
||||
.withUserConfiguration(MultipleAsyncTaskExecutorsConfiguration.class)
|
||||
.run((context) -> assertThat(context).getFailure()
|
||||
.rootCause()
|
||||
.isInstanceOf(FailureAnalyzedException.class)
|
||||
.isInstanceOf(BootstrapExecutorRequiredException.class)
|
||||
.message()
|
||||
.contains("bootstrap executor is required when 'spring.jpa.bootstrap' is set to 'async'"));
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.boot.jpa.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Exception thrown then the auto-configured
|
||||
* {@link LocalContainerEntityManagerFactoryBean} is missing a required bootstrap
|
||||
* executor.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public class BootstrapExecutorRequiredException extends IllegalStateException {
|
||||
|
||||
private final @Nullable String propertyName;
|
||||
|
||||
private final @Nullable String propertyValue;
|
||||
|
||||
public BootstrapExecutorRequiredException(String message) {
|
||||
this(message, null);
|
||||
}
|
||||
|
||||
public BootstrapExecutorRequiredException(String message, @Nullable Throwable cause) {
|
||||
this(message, null, null, cause);
|
||||
}
|
||||
|
||||
private BootstrapExecutorRequiredException(String message, @Nullable String propertyName,
|
||||
@Nullable String propertyValue, @Nullable Throwable cause) {
|
||||
super(message, cause);
|
||||
this.propertyName = propertyName;
|
||||
this.propertyValue = propertyValue;
|
||||
}
|
||||
|
||||
@Nullable String getPropertyName() {
|
||||
return this.propertyName;
|
||||
}
|
||||
|
||||
@Nullable String getPropertyValue() {
|
||||
return this.propertyValue;
|
||||
}
|
||||
|
||||
public static BootstrapExecutorRequiredException ofProperty(String name, String value) {
|
||||
Assert.hasText(name, "'name' must not be empty");
|
||||
Assert.hasText(value, "'value' must not be empty");
|
||||
String message = "An EntityManagerFactoryBean bootstrap executor is required when '%s' is set to '%s'"
|
||||
.formatted(name, value);
|
||||
return new BootstrapExecutorRequiredException(message, name, value, null);
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.boot.jpa.autoconfigure;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
import org.springframework.boot.diagnostics.FailureAnalyzer;
|
||||
|
||||
/**
|
||||
* {@link FailureAnalyzer} for {@link BootstrapExecutorRequiredException}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BootstrapExecutorRequiredFailureAnalyzer extends AbstractFailureAnalyzer<BootstrapExecutorRequiredException> {
|
||||
|
||||
@Override
|
||||
protected @Nullable FailureAnalysis analyze(Throwable rootFailure, BootstrapExecutorRequiredException cause) {
|
||||
StringBuilder action = new StringBuilder();
|
||||
action.append(actionPreamble(cause.getPropertyName()));
|
||||
action.append("\t- With an auto-configured task executor "
|
||||
+ "(you may need to set 'spring.task.execution.mode' to 'force').\n");
|
||||
action.append("\t- With an AsyncTaskExecutor bean named 'applicationTaskExecutor'.\n");
|
||||
action.append("\t- Using a EntityManagerFactoryBuilderCustomizer.\n");
|
||||
return new FailureAnalysis(cause.getMessage(), action.toString(), cause);
|
||||
}
|
||||
|
||||
private String actionPreamble(@Nullable String propertyName) {
|
||||
return (propertyName != null)
|
||||
? "Use a different '%s' value or provide a bootstrap executor using one of the following methods:\n"
|
||||
.formatted(propertyName)
|
||||
: "Provide a bootstrap executor using one of the following methods:\n";
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -130,7 +130,7 @@ public abstract class JpaBaseConfiguration {
|
||||
this::buildJpaProperties, persistenceUnitManager.getIfAvailable(), null, bootstrapExecutor);
|
||||
if (this.properties.getBootstrap() == Bootstrap.ASYNC) {
|
||||
builder.requireBootstrapExecutor(
|
||||
new PropertyBasedRequiredBackgroundBootstrapping("spring.jpa.bootstrap", "async"));
|
||||
() -> BootstrapExecutorRequiredException.ofProperty("spring.jpa.bootstrap", "async"));
|
||||
}
|
||||
customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
|
||||
return builder;
|
||||
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.boot.jpa.autoconfigure;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.boot.diagnostics.FailureAnalyzedException;
|
||||
import org.springframework.boot.jpa.EntityManagerFactoryBuilder;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Supplier} to use with
|
||||
* {@link EntityManagerFactoryBuilder#requireBootstrapExecutor} when a property indicates
|
||||
* background bootstrapping is required.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public class PropertyBasedRequiredBackgroundBootstrapping implements Supplier<RuntimeException> {
|
||||
|
||||
private final String propertyName;
|
||||
|
||||
private final String propertyValue;
|
||||
|
||||
public PropertyBasedRequiredBackgroundBootstrapping(String propertyName, String propertyValue) {
|
||||
Assert.notNull(propertyName, "'propertyName' must not be null");
|
||||
Assert.notNull(propertyValue, "'propertyValue' must not be null");
|
||||
this.propertyName = propertyName;
|
||||
this.propertyValue = propertyValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeException get() {
|
||||
String description = "A LocalContainerEntityManagerFactoryBean bootstrap executor is required when '%s' is set to '%s'"
|
||||
.formatted(this.propertyName, this.propertyValue);
|
||||
StringBuilder action = new StringBuilder();
|
||||
action.append("Use a different '%s' or provide a bootstrap executor using one of the following methods:\n"
|
||||
.formatted(this.propertyName));
|
||||
action.append("\tWith an auto-configured task executor "
|
||||
+ "(you may need to set 'spring.task.execution.mode' to 'force').");
|
||||
action.append("\tWith an AsyncTaskExecutor bean named 'applicationTaskExecutor.'");
|
||||
action.append("\tUsing a EntityManagerFactoryBuilderCustomizer.");
|
||||
return new FailureAnalyzedException(description, action.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
# Failure Analyzers
|
||||
org.springframework.boot.diagnostics.FailureAnalyzer=\
|
||||
org.springframework.boot.jpa.autoconfigure.BootstrapExecutorRequiredFailureAnalyzer
|
||||
|
||||
# Database Initializer Detectors
|
||||
org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector=\
|
||||
org.springframework.boot.jpa.JpaDatabaseInitializerDetector
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.boot.jpa.autoconfigure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.diagnostics.FailureAnalysis;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link BootstrapExecutorRequiredFailureAnalyzer}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class BootstrapExecutorRequiredFailureAnalyzerTests {
|
||||
|
||||
private final BootstrapExecutorRequiredFailureAnalyzer analyzer = new BootstrapExecutorRequiredFailureAnalyzer();
|
||||
|
||||
@Test
|
||||
void analyzeWhenBootstrapExecutorRequiredExceptionWithProperties() {
|
||||
BootstrapExecutorRequiredException exception = BootstrapExecutorRequiredException.ofProperty("testname",
|
||||
"testvalue");
|
||||
FailureAnalysis result = this.analyzer.analyze(exception);
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getDescription()).isEqualTo(
|
||||
"An EntityManagerFactoryBean bootstrap executor is required when 'testname' is set to 'testvalue'");
|
||||
assertThat(result.getAction()).isEqualTo(
|
||||
"""
|
||||
Use a different 'testname' value or provide a bootstrap executor using one of the following methods:
|
||||
- With an auto-configured task executor (you may need to set 'spring.task.execution.mode' to 'force').
|
||||
- With an AsyncTaskExecutor bean named 'applicationTaskExecutor'.
|
||||
- Using a EntityManagerFactoryBuilderCustomizer.
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeWhenBootstrapExecutorRequiredExceptionWithMessage() {
|
||||
BootstrapExecutorRequiredException exception = new BootstrapExecutorRequiredException("A custom message");
|
||||
FailureAnalysis result = this.analyzer.analyze(exception);
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getDescription()).isEqualTo("A custom message");
|
||||
assertThat(result.getAction()).isEqualTo(
|
||||
"""
|
||||
Provide a bootstrap executor using one of the following methods:
|
||||
- With an auto-configured task executor (you may need to set 'spring.task.execution.mode' to 'force').
|
||||
- With an AsyncTaskExecutor bean named 'applicationTaskExecutor'.
|
||||
- Using a EntityManagerFactoryBuilderCustomizer.
|
||||
""");
|
||||
}
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.boot.jpa.autoconfigure;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.diagnostics.FailureAnalyzedException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link PropertyBasedRequiredBackgroundBootstrapping}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PropertyBasedRequiredBackgroundBootstrappingTests {
|
||||
|
||||
@Test
|
||||
void getReturnsFailureAnalyzableException() {
|
||||
RuntimeException exception = new PropertyBasedRequiredBackgroundBootstrapping("test.bootstrap", "true").get();
|
||||
assertThat(exception).isInstanceOf(FailureAnalyzedException.class)
|
||||
.hasMessage("A LocalContainerEntityManagerFactoryBean bootstrap executor is required "
|
||||
+ "when 'test.bootstrap' is set to 'true'");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user