Skip to content

PropertyBatchUpdateException: causes of nested PropertyAccessExceptions not shown in output #34691

Closed
@rursprung

Description

@rursprung

when a PropertyBatchUpdateException gets instantiated it does not pass the PropertyAccessExceptions as cause to its super (BeansException), instead storing it locally in propertyAccessExceptions:

/**
* Create a new PropertyBatchUpdateException.
* @param propertyAccessExceptions the List of PropertyAccessExceptions
*/
public PropertyBatchUpdateException(PropertyAccessException[] propertyAccessExceptions) {
super(null, null);
Assert.notEmpty(propertyAccessExceptions, "At least 1 PropertyAccessException required");
this.propertyAccessExceptions = propertyAccessExceptions;
}

this makes sense since propertyAccessExceptions is a list while cause can only be a single exception.
however, the output of this is really nondescript and does not contain the actual root cause:

Error creating bean with name 'exampleRepository' defined in org.example.repository.exampleRepository defined in @EnableElasticsearchRepositories declared on OpenSearchConfiguration: Failed properties: Property 'elasticsearchOperations' threw exception
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'exampleRepository' defined in org.example.repository.exampleRepository defined in @EnableElasticsearchRepositories declared on OpenSearchConfiguration: Failed properties: Property 'elasticsearchOperations' threw exception
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1746)
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1460)
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600)
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
	at app//org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
	at app//org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:346)
	at app//org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
	at app//org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1149)
	at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1121)
	at app//org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1056)
	at app//org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)
	at app//org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)
	at app//org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
	at app//org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
	at app//org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
	at app//org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
	at app//org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
	at app//org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
	at app//org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
	at app//org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1461)
	at app//org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:553)
	at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
	at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108)
	at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225)
	at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152)
	at app//org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130)
	at app//org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener.injectFields(BeanOverrideTestExecutionListener.java:93)
	at app//org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener.prepareTestInstance(BeanOverrideTestExecutionListener.java:64)
	at app//org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:260)
	at app//org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:160)
	at java.base@21.0.5/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
	at java.base@21.0.5/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base@21.0.5/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base@21.0.5/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base@21.0.5/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1708)
	at java.base@21.0.5/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base@21.0.5/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base@21.0.5/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
	at java.base@21.0.5/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
	at java.base@21.0.5/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base@21.0.5/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at java.base@21.0.5/java.util.Optional.orElseGet(Optional.java:364)
	at java.base@21.0.5/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'elasticsearchOperations' threw exception
	at app//org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:135)
	at app//org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:79)
	at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1743)
	... 43 more

as you can see it includes PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'elasticsearchOperations' threw exception which comes from its name:

super(propertyChangeEvent, "Property '" + propertyChangeEvent.getPropertyName() + "' threw exception", cause);

however it doesn't say what that exception (i.e. its cause) is. it has a cause set (i checked with the debugger), it's just a matter of also printing it. i presume the formatting for printing is done by PropertyBatchUpdateException#toString:

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getName()).append("; nested PropertyAccessExceptions (");
sb.append(getExceptionCount()).append(") are:");
for (int i = 0; i < this.propertyAccessExceptions.length; i++) {
sb.append('\n').append("PropertyAccessException ").append(i + 1).append(": ");
sb.append(this.propertyAccessExceptions[i]);
}
return sb.toString();
}

it seems that the standard PropertyAccessException#toString does not include the cause, so maybe that needs to be handled by PropertyBatchUpdateException#toString?

(i didn't create a minimum repro for this as i think the affected code is quite obvious from the callstacks & source links provided)

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions