Skip to content

Commit

Permalink
Replace deep exception message nesting with custom inclusion of cause…
Browse files Browse the repository at this point in the history
… messages

Includes deprecation of NestedServletException, whereas NestedCheckedException and NestedRuntimeException remain as base classes with several convenience methods.

Closes gh-25162
  • Loading branch information
jhoeller committed Jun 14, 2022
1 parent 933965b commit 4e1b9f1
Show file tree
Hide file tree
Showing 31 changed files with 78 additions and 245 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -71,7 +71,8 @@ public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, @Nullable
(requiredType != null ?
" to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") +
(propertyChangeEvent.getPropertyName() != null ?
" for property '" + propertyChangeEvent.getPropertyName() + "'" : ""),
" for property '" + propertyChangeEvent.getPropertyName() + "'" : "") +
(cause != null ? "; " + cause.getMessage() : ""),
cause);
this.propertyName = propertyChangeEvent.getPropertyName();
this.value = propertyChangeEvent.getNewValue();
Expand All @@ -97,7 +98,8 @@ public TypeMismatchException(@Nullable Object value, @Nullable Class<?> required
*/
public TypeMismatchException(@Nullable Object value, @Nullable Class<?> requiredType, @Nullable Throwable cause) {
super("Failed to convert value of type '" + ClassUtils.getDescriptiveType(value) + "'" +
(requiredType != null ? " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : ""),
(requiredType != null ? " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") +
(cause != null ? "; " + cause.getMessage() : ""),
cause);
this.value = value;
this.requiredType = requiredType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -62,7 +62,7 @@ public UnsatisfiedDependencyException(
public UnsatisfiedDependencyException(
@Nullable String resourceDescription, @Nullable String beanName, String propertyName, BeansException ex) {

this(resourceDescription, beanName, propertyName, "");
this(resourceDescription, beanName, propertyName, ex.getMessage());
initCause(ex);
}

Expand Down Expand Up @@ -94,7 +94,7 @@ public UnsatisfiedDependencyException(
public UnsatisfiedDependencyException(
@Nullable String resourceDescription, @Nullable String beanName, @Nullable InjectionPoint injectionPoint, BeansException ex) {

this(resourceDescription, beanName, injectionPoint, "");
this(resourceDescription, beanName, injectionPoint, ex.getMessage());
initCause(ex);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -86,7 +86,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
throw new BeanInitializationException("Could not load properties: " + ex.getMessage(), ex);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,7 @@ protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}

Expand Down Expand Up @@ -1302,8 +1301,7 @@ protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}

Expand Down Expand Up @@ -1699,8 +1697,7 @@ else if (convertible && originalValue instanceof TypedStringValue &&
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}

Expand Down Expand Up @@ -1752,8 +1749,7 @@ protected Object initializeBean(String beanName, Object bean, @Nullable RootBean
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
(mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,7 @@ private Object instantiate(
return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean instantiation via constructor failed", ex);
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}

Expand Down Expand Up @@ -631,8 +630,7 @@ private Object instantiate(String beanName, RootBeanDefinition mbd,
mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean instantiation via factory method failed", ex);
throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -152,7 +152,7 @@ public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, Bean
"Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex);
}
catch (InvocationTargetException ex) {
String msg = "Factory method '" + factoryMethod.getName() + "' threw exception";
String msg = ex.getTargetException().getMessage();
if (bd.getFactoryBeanName() != null && owner instanceof ConfigurableBeanFactory &&
((ConfigurableBeanFactory) owner).isCurrentlyInCreation(bd.getFactoryBeanName())) {
msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
configClass.getMetadata().getClassName() + "]: " + ex.getMessage(), ex);
}
finally {
this.importStack.pop();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -155,7 +155,7 @@ public void afterSingletonsInstantiated() {
}
catch (Throwable ex) {
throw new BeanInitializationException("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "'", ex);
"annotation on bean with name '" + beanName + "': " + ex.getMessage(), ex);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -944,8 +944,8 @@ void relatedCausesFromConstructorResolution() {
xbf.getBean("rod2Accessor");
}
catch (BeanCreationException ex) {
assertThat(ex.toString().contains("touchy")).isTrue();
ex.printStackTrace();
assertThat(ex.toString().contains("touchy")).isTrue();
assertThat((Object) ex.getRelatedCauses()).isNull();
}
}
Expand Down Expand Up @@ -1370,7 +1370,7 @@ void rejectsOverrideOfBogusMethodName() {
reader.loadBeanDefinitions(INVALID_NO_SUCH_METHOD_CONTEXT);
assertThatExceptionOfType(BeanDefinitionStoreException.class).isThrownBy(() ->
xbf.getBean("constructorOverrides"))
.withMessageContaining("bogusMethod");
.satisfies(ex -> ex.getCause().getMessage().contains("bogusMethod"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -56,7 +56,7 @@ public void componentTwoConstructorsNoHint() {
public void componentTwoSpecificConstructorsNoHint() {
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(() ->
new AnnotationConfigApplicationContext(BaseConfiguration.class, TwoSpecificConstructorsComponent.class))
.withMessageContaining(NoSuchMethodException.class.getName());
.withMessageContaining("No default constructor found");
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,13 @@
* @author Rod Johnson
* @author Juergen Hoeller
* @see #getMessage
* @see #printStackTrace
* @see NestedRuntimeException
*/
public abstract class NestedCheckedException extends Exception {

/** Use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 7100714597678207546L;

static {
// Eagerly load the NestedExceptionUtils class to avoid classloader deadlock
// issues on OSGi when calling getMessage(). Reported by Don Brown; SPR-5607.
NestedExceptionUtils.class.getName();
}


/**
* Construct a {@code NestedCheckedException} with the specified detail message.
Expand All @@ -67,17 +60,6 @@ public NestedCheckedException(@Nullable String msg, @Nullable Throwable cause) {
}


/**
* Return the detail message, including the message from the nested exception
* if there is one.
*/
@Override
@Nullable
public String getMessage() {
return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
}


/**
* Retrieve the innermost cause of this exception, if any.
* @return the innermost exception, or {@code null} if none
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 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.
Expand Down Expand Up @@ -29,8 +29,6 @@
* @since 2.0
* @see NestedRuntimeException
* @see NestedCheckedException
* @see NestedIOException
* @see org.springframework.web.util.NestedServletException
*/
public abstract class NestedExceptionUtils {

Expand All @@ -39,7 +37,10 @@ public abstract class NestedExceptionUtils {
* @param message the base message
* @param cause the root cause
* @return the full exception message
* @deprecated as of 6.0, in favor of custom exception messages
* with selective inclusion of cause messages
*/
@Deprecated
@Nullable
public static String buildMessage(@Nullable String message, @Nullable Throwable cause) {
if (cause == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,13 @@
* @author Rod Johnson
* @author Juergen Hoeller
* @see #getMessage
* @see #printStackTrace
* @see NestedCheckedException
*/
public abstract class NestedRuntimeException extends RuntimeException {

/** Use serialVersionUID from Spring 1.2 for interoperability. */
private static final long serialVersionUID = 5439915454935047936L;

static {
// Eagerly load the NestedExceptionUtils class to avoid classloader deadlock
// issues on OSGi when calling getMessage(). Reported by Don Brown; SPR-5607.
NestedExceptionUtils.class.getName();
}


/**
* Construct a {@code NestedRuntimeException} with the specified detail message.
Expand All @@ -67,17 +60,6 @@ public NestedRuntimeException(@Nullable String msg, @Nullable Throwable cause) {
}


/**
* Return the detail message, including the message from the nested exception
* if there is one.
*/
@Override
@Nullable
public String getMessage() {
return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
}


/**
* Retrieve the innermost cause of this exception, if any.
* @return the innermost exception, or {@code null} if none
Expand Down
Loading

0 comments on commit 4e1b9f1

Please sign in to comment.