From 823e197b983d247ee56f778a594a3f297d2bd840 Mon Sep 17 00:00:00 2001 From: Andriy Dmytruk Date: Tue, 3 Dec 2024 17:29:27 -0500 Subject: [PATCH 1/4] Improve the dependency path exception message by printing each segment on a new line. This improves the readability of message for long paths, large classnames or classes with many parameters, as each class from the path is immediately visible. I changed the tests to use longer classnames, which is more realistic. --- .../ConstructorDependencyFailureSpec.groovy | 24 +++++++------- .../failures/ConstructorExceptionSpec.groovy | 25 +++++++------- .../FieldDependencyMissingFailureSpec.groovy | 22 ++++++++----- .../NestedDependencyFailureSpec.groovy | 33 +++++++++++-------- .../PostConstructExceptionSpec.groovy | 28 ++++++++-------- .../PropertyDependencyMissingSpec.groovy | 21 +++++++----- .../failures/PropertyExceptionSpec.groovy | 22 +++++++------ .../ConstructorDependencyFailureSpec.groovy | 16 +++++---- .../{A.java => MyClassA.java} | 2 +- .../{B.java => MyClassB.java} | 8 ++--- .../ConstructorExceptionSpec.groovy | 9 +++-- .../ctorexception/{A.java => MyClassA.java} | 4 +-- .../ctorexception/{B.java => MyClassB.java} | 8 ++--- .../ctorexception/{C.java => MyClassC.java} | 4 +-- .../FieldDependencyMissingFailureSpec.groovy | 14 +++++--- .../{A.java => MyClassA.java} | 2 +- .../{B.java => MyClassB.java} | 8 ++--- .../{A.java => MyClassA.java} | 4 +-- .../{B.java => MyClassB.java} | 8 ++--- .../{C.java => MyClassC.java} | 4 +-- .../{D.java => MyClassD.java} | 2 +- .../NestedDependencyFailureSpec.groovy | 17 ++++++---- .../PostConstructExceptionSpec.groovy | 10 +++--- .../AbstractBeanResolutionContext.java | 10 ++++-- .../context/exceptions/MessageUtils.java | 2 +- 25 files changed, 174 insertions(+), 133 deletions(-) rename inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/{A.java => MyClassA.java} (95%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/{B.java => MyClassB.java} (85%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/{A.java => MyClassA.java} (93%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/{B.java => MyClassB.java} (86%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/{C.java => MyClassC.java} (94%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/{A.java => MyClassA.java} (95%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/{B.java => MyClassB.java} (86%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/{A.java => MyClassA.java} (93%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/{B.java => MyClassB.java} (86%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/{C.java => MyClassC.java} (93%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/{D.java => MyClassD.java} (96%) diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorDependencyFailureSpec.groovy index b920444e69d..0f19de31fda 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorDependencyFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorDependencyFailureSpec.groovy @@ -28,33 +28,35 @@ class ConstructorDependencyFailureSpec extends Specification { void "test a useful exception is thrown when a dependency injection failure occurs"() { given: ApplicationContext context = ApplicationContext.run() + var space = " " when:"A bean that defines a constructor dependency on a missing bean" - B b = context.getBean(B) + context.getBean(MyClassB) then:"The correct error is thrown" def e = thrown(DependencyInjectionException) - e.message.normalize().contains('''\ -Failed to inject value for parameter [a] of class: io.micronaut.inject.failures.ConstructorDependencyFailureSpec$B + e.message.normalize() == """\ +Failed to inject value for parameter [propA] of class: io.micronaut.inject.failures.ConstructorDependencyFailureSpec\$MyClassB -Message: No bean of type [io.micronaut.inject.failures.ConstructorDependencyFailureSpec$A] exists.''') - - e.message.normalize().contains('Path Taken: new B(A a) --> new B([A a])') +Message: No bean of type [io.micronaut.inject.failures.ConstructorDependencyFailureSpec\$MyClassA] exists.$space +Path Taken:$space +new MyClassB(MyClassA propA) +\\---> new MyClassB([MyClassA propA])""" cleanup: context.close() } - static interface A { + static interface MyClassA { } - static class B { - private final A a + static class MyClassB { + private final MyClassA propA @Inject - B(A a) { - this.a = a + MyClassB(MyClassA propA) { + this.propA = propA } } diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorExceptionSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorExceptionSpec.groovy index a1352adf733..0f04e485a15 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorExceptionSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorExceptionSpec.groovy @@ -30,40 +30,43 @@ class ConstructorExceptionSpec extends Specification { ApplicationContext context = ApplicationContext.run() when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + MyClassB b = context.getBean(MyClassB) then:"The implementation is injected" def e = thrown(BeanInstantiationException) //e.cause.message == 'bad' e.message.normalize() == '''\ -Error instantiating bean of type [io.micronaut.inject.failures.ConstructorExceptionSpec$A] +Error instantiating bean of type [io.micronaut.inject.failures.ConstructorExceptionSpec$MyClassA] Message: bad -Path Taken: new B() --> B.a --> new A([C c])''' +Path Taken: +new MyClassB() +\\---> MyClassB.propA + \\---> new MyClassA([MyClassC propC])''' cleanup: context.close() } @Singleton - static class C { - C() { + static class MyClassC { + MyClassC() { throw new RuntimeException("bad") } } @Singleton - static class A { - A(C c) { + static class MyClassA { + MyClassA(MyClassC propC) { } } - static class B { + static class MyClassB { @Inject - private A a + private MyClassA propA - A getA() { - return this.a + MyClassA getA() { + return this.propA } } diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldDependencyMissingFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldDependencyMissingFailureSpec.groovy index 61e68771517..21b10f84457 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldDependencyMissingFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldDependencyMissingFailureSpec.groovy @@ -28,29 +28,35 @@ class FieldDependencyMissingFailureSpec extends Specification { void "test injection via setter with interface"() { given: ApplicationContext context = ApplicationContext.run() + var space = " " when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + context.getBean(MyClassB) then:"The implementation is injected" DependencyInjectionException e = thrown() - e.message.normalize().contains 'Failed to inject value for field [a] of class: io.micronaut.inject.failures.FieldDependencyMissingFailureSpec$B' - e.message.normalize().contains 'Path Taken: new B() --> B.a' + e.message.normalize() == """\ +Failed to inject value for field [propA] of class: io.micronaut.inject.failures.FieldDependencyMissingFailureSpec\$MyClassB + +Message: No bean of type [io.micronaut.inject.failures.FieldDependencyMissingFailureSpec\$MyClassA] exists.$space +Path Taken:$space +new MyClassB() +\\---> MyClassB.propA""" cleanup: context.close() } - static interface A { + static interface MyClassA { } - static class B { + static class MyClassB { @Inject - private A a + private MyClassA propA - A getA() { - return this.a + MyClassA getPropA() { + return this.propA } } diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/NestedDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/NestedDependencyFailureSpec.groovy index 7da9a926f43..1da97c45cf4 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/NestedDependencyFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/NestedDependencyFailureSpec.groovy @@ -28,44 +28,49 @@ class NestedDependencyFailureSpec extends Specification { void "test injection via setter with interface"() { given: ApplicationContext context = ApplicationContext.run() + var space = " " when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + context.getBean(MyClassB) then:"The implementation is injected" DependencyInjectionException e = thrown() - e.message.normalize().contains( '''\ -Failed to inject value for parameter [d] of class: io.micronaut.inject.failures.NestedDependencyFailureSpec$C + e.message.normalize() == """\ +Failed to inject value for parameter [propD] of class: io.micronaut.inject.failures.NestedDependencyFailureSpec\$MyClassC -Message: No bean of type [io.micronaut.inject.failures.NestedDependencyFailureSpec$D] exists.''') - e.message.normalize().contains('Path Taken: new B() --> B.a --> new A([C c]) --> new C([D d])') +Message: No bean of type [io.micronaut.inject.failures.NestedDependencyFailureSpec\$MyClassD] exists.$space +Path Taken:$space +new MyClassB() +\\---> MyClassB.propA + \\---> new MyClassA([MyClassC propC]) + \\---> new MyClassC([MyClassD propD])""" cleanup: context.close() } - static class D {} + static class MyClassD {} @Singleton - static class C { - C(D d) { + static class MyClassC { + MyClassC(MyClassD propD) { } } @Singleton - static class A { - A(C c) { + static class MyClassA { + MyClassA(MyClassC propC) { } } - static class B { + static class MyClassB { @Inject - private A a + private MyClassA propA - A getA() { - return this.a + MyClassA getA() { + return this.propA } } diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PostConstructExceptionSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PostConstructExceptionSpec.groovy index d41f25999a8..e863f84d2a8 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PostConstructExceptionSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PostConstructExceptionSpec.groovy @@ -32,39 +32,41 @@ class PostConstructExceptionSpec extends Specification { ApplicationContext context = ApplicationContext.run() when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + MyClassB b = context.getBean(MyClassB) then:"The implementation is injected" BeanInstantiationException e = thrown() - def ls = CachedEnvironment.getProperty("line.separator") - e.message == 'Error instantiating bean of type [io.micronaut.inject.failures.PostConstructExceptionSpec$B]' + ls + ls + - 'Message: bad' + ls + - 'Path Taken: new B()' + e.message.normalize() == '''\ +Error instantiating bean of type [io.micronaut.inject.failures.PostConstructExceptionSpec$MyClassB] + +Message: bad +Path Taken: +new MyClassB()''' cleanup: context.close() } @Singleton - static class A { + static class MyClassA { } @Singleton - static class B { + static class MyClassB { boolean setupComplete = false boolean injectedFirst = false - @Inject protected A another - private A a + @Inject protected MyClassA another + private MyClassA propA @Inject - void setA(A a ) { - this.a = a + void setPropA(MyClassA propA) { + this.propA = propA } - A getA() { - return a + MyClassA getPropA() { + return propA } @PostConstruct diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyDependencyMissingSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyDependencyMissingSpec.groovy index d818d4f3db9..bccde7a15a6 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyDependencyMissingSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyDependencyMissingSpec.groovy @@ -28,29 +28,32 @@ class PropertyDependencyMissingSpec extends Specification { void "test a useful exception is thrown when a dependency injection failure occurs"() { given: ApplicationContext context = ApplicationContext.run() + var space = " " when:"A bean is obtained that has a setter with @Inject" - context.getBean(B) + context.getBean(MyClassB) then:"The correct error is thrown" DependencyInjectionException e = thrown() - def lines = e.message.lines().toList() - lines[0] == 'Failed to inject value for parameter [a] of method [setA] of class: io.micronaut.inject.failures.PropertyDependencyMissingSpec$B' - lines[1] == '' - lines[2] == 'Message: No bean of type [io.micronaut.inject.failures.PropertyDependencyMissingSpec$A] exists. ' - lines[3] == 'Path Taken: new B() --> B.setA([A a])' + e.message.normalize() == """\ +Failed to inject value for parameter [propA] of method [setPropA] of class: io.micronaut.inject.failures.PropertyDependencyMissingSpec\$MyClassB + +Message: No bean of type [io.micronaut.inject.failures.PropertyDependencyMissingSpec\$MyClassA] exists.$space +Path Taken:$space +new MyClassB() +\\---> MyClassB.setPropA([MyClassA propA])""" cleanup: context.close() } - static interface A { + static interface MyClassA { } - static class B { + static class MyClassB { @Inject - A a + MyClassA propA } } diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyExceptionSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyExceptionSpec.groovy index 62dbef99de8..68d8461fe62 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyExceptionSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyExceptionSpec.groovy @@ -31,38 +31,40 @@ class PropertyExceptionSpec extends Specification { ApplicationContext context = ApplicationContext.run() when:"A bean is obtained that has a setter with @Inject" - context.getBean(B) + context.getBean(MyClassB) then:"The implementation is injected" BeanInstantiationException e = thrown() e.cause.message == 'bad' e.message.normalize() == '''\ -Error instantiating bean of type [io.micronaut.inject.failures.PropertyExceptionSpec$B] +Error instantiating bean of type [io.micronaut.inject.failures.PropertyExceptionSpec$MyClassB] Message: bad -Path Taken: new B() --> B.a''' +Path Taken: +new MyClassB() +\\---> MyClassB.propA''' cleanup: context.close() } @Singleton - static class C { + static class MyClassC { } @Singleton - static class A { + static class MyClassA { @Inject - void setC(C c) { + void setC(MyClassC propC) { throw new RuntimeException("bad") } } - static class B { + static class MyClassB { @Inject - private A a + private MyClassA propA - A getA() { - return this.a + MyClassA getPropA() { + return this.propA } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/ConstructorDependencyFailureSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/ConstructorDependencyFailureSpec.groovy index f40303e97c8..923eee053fd 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/ConstructorDependencyFailureSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/ConstructorDependencyFailureSpec.groovy @@ -24,20 +24,22 @@ class ConstructorDependencyFailureSpec extends Specification { void "test a useful exception is thrown when a dependency injection failure occurs"() { given: ApplicationContext context = ApplicationContext.run() + var space = " " when:"A bean that defines a constructor dependency on a missing bean" - B b = context.getBean(B) + context.getBean(MyClassB) then:"The correct error is thrown" DependencyInjectionException e = thrown() - e.message.normalize().contains('''\ -Failed to inject value for parameter [a] of class: io.micronaut.inject.failures.ctordependencyfailure.B + e.message.normalize() == """\ +Failed to inject value for parameter [propA] of class: io.micronaut.inject.failures.ctordependencyfailure.MyClassB -Message: No bean of type [io.micronaut.inject.failures.ctordependencyfailure.A] exists.''') - - e.message.normalize().contains('Path Taken: new B(A a) --> new B([A a])') +Message: No bean of type [io.micronaut.inject.failures.ctordependencyfailure.MyClassA] exists.$space +Path Taken:$space +new MyClassB(MyClassA propA) +\\---> new MyClassB([MyClassA propA])""" cleanup: context.close() } -} \ No newline at end of file +} diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/A.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/MyClassA.java similarity index 95% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/A.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/MyClassA.java index 5d545bc0885..ea303bfcd3b 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/A.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/MyClassA.java @@ -15,5 +15,5 @@ */ package io.micronaut.inject.failures.ctordependencyfailure; -public interface A { +public interface MyClassA { } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/B.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/MyClassB.java similarity index 85% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/B.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/MyClassB.java index 9bc84aaabc1..83a7f3d9c25 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/B.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/MyClassB.java @@ -17,11 +17,11 @@ import jakarta.inject.Inject; -public class B { - private final A a; +public class MyClassB { + private final MyClassA propA; @Inject - public B(A a) { - this.a = a; + public MyClassB(MyClassA propA) { + this.propA = propA; } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/ConstructorExceptionSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/ConstructorExceptionSpec.groovy index d86150d2589..c54277e7369 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/ConstructorExceptionSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/ConstructorExceptionSpec.groovy @@ -26,16 +26,19 @@ class ConstructorExceptionSpec extends Specification { ApplicationContext context = ApplicationContext.run(["spec.name": getClass().simpleName]) when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + MyClassB b = context.getBean(MyClassB) then:"The implementation is injected" def e = thrown(BeanInstantiationException) //e.cause.message == 'bad' e.message.normalize() == '''\ -Error instantiating bean of type [io.micronaut.inject.failures.ctorexception.A] +Error instantiating bean of type [io.micronaut.inject.failures.ctorexception.MyClassA] Message: bad -Path Taken: new B() --> B.a --> new A([C c])''' +Path Taken: +new MyClassB() +\\---> MyClassB.propA + \\---> new MyClassA([MyClassC propC])''' cleanup: context.close() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/A.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassA.java similarity index 93% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/A.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassA.java index 125fbc96b1b..e1e48a64438 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/A.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassA.java @@ -21,7 +21,7 @@ @Requires(property = "spec.name", value = "ConstructorExceptionSpec") @Singleton -public class A { - public A(C c) { +public class MyClassA { + public MyClassA(MyClassC propC) { } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/B.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassB.java similarity index 86% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/B.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassB.java index e9b963f981c..ef96b4c92bb 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/B.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassB.java @@ -17,11 +17,11 @@ import jakarta.inject.Inject; -public class B { +public class MyClassB { @Inject - private A a; + private MyClassA propA; - public A getA() { - return this.a; + public MyClassA getPropA() { + return this.propA; } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/C.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassC.java similarity index 94% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/C.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassC.java index 723d52613ff..8f3cf6eaed0 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/C.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/MyClassC.java @@ -21,8 +21,8 @@ @Requires(property = "spec.name", value = "ConstructorExceptionSpec") @Singleton -public class C { - public C() { +public class MyClassC { + public MyClassC() { throw new RuntimeException("bad"); } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/FieldDependencyMissingFailureSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/FieldDependencyMissingFailureSpec.groovy index 22d49524613..faee15ea15b 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/FieldDependencyMissingFailureSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/FieldDependencyMissingFailureSpec.groovy @@ -25,16 +25,20 @@ class FieldDependencyMissingFailureSpec extends Specification { void "test injection via setter with interface"() { given: ApplicationContext context = ApplicationContext.run() + var space = " " when:"A bean is obtained that has a setter with @Inject" - context.getBean(B) + context.getBean(MyClassB) then:"The implementation is injected" DependencyInjectionException e = thrown() - e.message.normalize().contains('''\ -Failed to inject value for field [a] of class: io.micronaut.inject.failures.fielddependencymissing.B -''') - e.message.normalize().contains('''Path Taken: new B() --> B.a''') + e.message.normalize() == """\ +Failed to inject value for field [propA] of class: io.micronaut.inject.failures.fielddependencymissing.MyClassB + +Message: No bean of type [io.micronaut.inject.failures.fielddependencymissing.MyClassA] exists.$space +Path Taken:$space +new MyClassB() +\\---> MyClassB.propA""" cleanup: context.close() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/A.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/MyClassA.java similarity index 95% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/A.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/MyClassA.java index eb74cea10b8..8274236a5fd 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/A.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/MyClassA.java @@ -15,5 +15,5 @@ */ package io.micronaut.inject.failures.fielddependencymissing; -public interface A { +public interface MyClassA { } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/B.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/MyClassB.java similarity index 86% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/B.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/MyClassB.java index 2f98528b7a1..e595e6f41c6 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/B.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/MyClassB.java @@ -17,11 +17,11 @@ import jakarta.inject.Inject; -public class B { +public class MyClassB { @Inject - private A a; + private MyClassA propA; - public A getA() { - return this.a; + public MyClassA getPropA() { + return this.propA; } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/A.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassA.java similarity index 93% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/A.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassA.java index 483c876d9c9..f438b2892c8 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/A.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassA.java @@ -21,8 +21,8 @@ @Requires(property = "spec.name", value = "NestedDependencyFailureSpec") @Singleton -public class A { - public A(C c) { +public class MyClassA { + public MyClassA(MyClassC propC) { } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/B.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassB.java similarity index 86% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/B.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassB.java index f49b6c711e9..6b64cad4849 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/B.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassB.java @@ -17,11 +17,11 @@ import jakarta.inject.Inject; -public class B { +public class MyClassB { @Inject - private A a; + private MyClassA propA; - public A getA() { - return this.a; + public MyClassA getPropA() { + return this.propA; } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/C.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassC.java similarity index 93% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/C.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassC.java index 60f293f6e56..bb5e66d85a0 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/C.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassC.java @@ -21,8 +21,8 @@ @Requires(property = "spec.name", value = "NestedDependencyFailureSpec") @Singleton -public class C { - public C(D d) { +public class MyClassC { + public MyClassC(MyClassD propD) { } } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/D.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassD.java similarity index 96% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/D.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassD.java index 233a098fde9..27f39a0dbd8 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/D.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/MyClassD.java @@ -15,5 +15,5 @@ */ package io.micronaut.inject.failures.nesteddependency; -public class D { +public class MyClassD { } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/NestedDependencyFailureSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/NestedDependencyFailureSpec.groovy index 18eb3c87a1e..6407268f342 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/NestedDependencyFailureSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/NestedDependencyFailureSpec.groovy @@ -16,8 +16,6 @@ package io.micronaut.inject.failures.nesteddependency import io.micronaut.context.ApplicationContext -import io.micronaut.context.BeanContext -import io.micronaut.context.DefaultBeanContext import io.micronaut.context.exceptions.DependencyInjectionException import spock.lang.Specification @@ -26,18 +24,23 @@ class NestedDependencyFailureSpec extends Specification { void "test injection via setter with interface"() { given: ApplicationContext context = ApplicationContext.run(["spec.name": getClass().simpleName]) + var space = " " when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + context.getBean(MyClassB) then:"The implementation is injected" def e = thrown(DependencyInjectionException) - e.message.normalize().contains( '''\ -Failed to inject value for parameter [d] of class: io.micronaut.inject.failures.nesteddependency.C + e.message.normalize() == """\ +Failed to inject value for parameter [propD] of class: io.micronaut.inject.failures.nesteddependency.MyClassC -Message: No bean of type [io.micronaut.inject.failures.nesteddependency.D] exists.''') - e.message.normalize().contains('Path Taken: new B() --> B.a --> new A([C c]) --> new C([D d])') +Message: No bean of type [io.micronaut.inject.failures.nesteddependency.MyClassD] exists.$space +Path Taken:$space +new MyClassB() +\\---> MyClassB.propA + \\---> new MyClassA([MyClassC propC]) + \\---> new MyClassC([MyClassD propD])""" cleanup: context.close() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/PostConstructExceptionSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/PostConstructExceptionSpec.groovy index 65589e304af..9e1b68851e5 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/PostConstructExceptionSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/PostConstructExceptionSpec.groovy @@ -31,10 +31,12 @@ class PostConstructExceptionSpec extends Specification { then:"The implementation is injected" def e = thrown(BeanInstantiationException) - def ls = CachedEnvironment.getProperty("line.separator") - e.message == 'Error instantiating bean of type [io.micronaut.inject.failures.postconstruct.B]' + ls + ls + - 'Message: bad' + ls + - 'Path Taken: new B()' + e.message.normalize() == '''\ +Error instantiating bean of type [io.micronaut.inject.failures.postconstruct.B] + +Message: bad +Path Taken: +new B()''' cleanup: context.close() diff --git a/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java b/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java index 9e88cc21e89..a3d9e555c99 100644 --- a/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java +++ b/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java @@ -301,7 +301,7 @@ private Map getAttributesOrCreate() { */ class DefaultPath extends LinkedList> implements Path { - public static final String RIGHT_ARROW = " --> "; + public static final String RIGHT_ARROW = "\\---> "; private static final String CIRCULAR_ERROR_MSG = "Circular dependency detected"; DefaultPath() { @@ -310,11 +310,15 @@ class DefaultPath extends LinkedList> implements Path { @Override public String toString() { Iterator> i = descendingIterator(); - StringBuilder pathString = new StringBuilder(); + String ls = CachedEnvironment.getProperty("line.separator"); + StringBuilder pathString = new StringBuilder().append(ls); + + String spaces = ""; while (i.hasNext()) { pathString.append(i.next().toString()); if (i.hasNext()) { - pathString.append(RIGHT_ARROW); + pathString.append(ls).append(spaces).append(RIGHT_ARROW); + spaces += " "; } } return pathString.toString(); diff --git a/inject/src/main/java/io/micronaut/context/exceptions/MessageUtils.java b/inject/src/main/java/io/micronaut/context/exceptions/MessageUtils.java index 58cbb9c8c50..e564d95e0b6 100644 --- a/inject/src/main/java/io/micronaut/context/exceptions/MessageUtils.java +++ b/inject/src/main/java/io/micronaut/context/exceptions/MessageUtils.java @@ -60,7 +60,7 @@ static String buildMessage(BeanResolutionContext resolutionContext, String messa } if (hasPath) { String pathString = path.toString(); - builder.append("Path Taken: ").append(pathString); + builder.append("Path Taken:").append(pathString); } return builder.toString(); } From 3c79507d1000b262dc7c85d5837184ca50e55b23 Mon Sep 17 00:00:00 2001 From: Andriy Dmytruk Date: Tue, 3 Dec 2024 18:35:01 -0500 Subject: [PATCH 2/4] Remove duplication --- .../io/micronaut/context/AbstractBeanResolutionContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java b/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java index a3d9e555c99..e8cfe93d65e 100644 --- a/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java +++ b/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java @@ -349,7 +349,7 @@ public String toCircularString() { pathString.append(ls).append(spaces).append("^").append(" \\---> "); spaces = spaces + "| "; } else if (index != 0) { - pathString.append(ls).append(spaces).append("\\---> "); + pathString.append(ls).append(spaces).append(RIGHT_ARROW); } pathString.append(segmentString); spaces = spaces + " "; From c5b8ae425ab06d7f7b2d17754832bbb526e75aaf Mon Sep 17 00:00:00 2001 From: Andriy Dmytruk Date: Thu, 5 Dec 2024 10:09:23 -0500 Subject: [PATCH 3/4] Use shortened fully-qualified names in dependency injection errors Fully-qualified names are clickable in IDE terminals, which improves usability. Shortened form prevents the messages from being too convoluted. --- .../io/micronaut/core/naming/NameUtils.java | 36 ++++++++ ...ructorCircularDependencyFailureSpec.groovy | 20 ++--- .../ConstructorDependencyFailureSpec.groovy | 4 +- .../failures/ConstructorExceptionSpec.groovy | 6 +- ...actoryCircularDependencyFailureSpec.groovy | 8 +- .../FactoryDependencyFailureSpec.groovy | 83 +++++++++++++++++++ .../FieldCircularDependencyFailureSpec.groovy | 24 +++--- .../FieldDependencyMissingFailureSpec.groovy | 4 +- .../NestedDependencyFailureSpec.groovy | 8 +- .../PostConstructExceptionSpec.groovy | 2 +- ...opertyCircularDependencyFailureSpec.groovy | 18 ++-- .../PropertyDependencyMissingSpec.groovy | 4 +- .../failures/PropertyExceptionSpec.groovy | 4 +- ...ructorCircularDependencyFailureSpec.groovy | 18 ++-- .../ConstructorDependencyFailureSpec.groovy | 4 +- .../ConstructorExceptionSpec.groovy | 6 +- .../FieldCircularDependencyFailureSpec.groovy | 14 ++-- .../{A.java => MyClassA.java} | 4 +- .../{B.java => MyClassB.java} | 4 +- .../{C.java => MyClassC.java} | 4 +- .../FieldDependencyMissingFailureSpec.groovy | 4 +- .../NestedDependencyFailureSpec.groovy | 8 +- .../postconstruct/{A.java => MyClassA.java} | 2 +- .../postconstruct/{B.java => MyClassB.java} | 14 ++-- .../PostConstructExceptionSpec.groovy | 7 +- .../AbstractBeanResolutionContext.java | 27 ++++-- 26 files changed, 235 insertions(+), 102 deletions(-) create mode 100644 inject-groovy/src/test/groovy/io/micronaut/inject/failures/FactoryDependencyFailureSpec.groovy rename inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/{A.java => MyClassA.java} (93%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/{B.java => MyClassB.java} (94%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/{C.java => MyClassC.java} (92%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/{A.java => MyClassA.java} (97%) rename inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/{B.java => MyClassB.java} (83%) diff --git a/core/src/main/java/io/micronaut/core/naming/NameUtils.java b/core/src/main/java/io/micronaut/core/naming/NameUtils.java index 4e8bbf0f765..60264538e5b 100644 --- a/core/src/main/java/io/micronaut/core/naming/NameUtils.java +++ b/core/src/main/java/io/micronaut/core/naming/NameUtils.java @@ -16,6 +16,7 @@ package io.micronaut.core.naming; import io.micronaut.core.annotation.AccessorsStyle; +import io.micronaut.core.annotation.Experimental; import io.micronaut.core.annotation.NonNull; import io.micronaut.core.util.ArgumentUtils; import io.micronaut.core.util.StringUtils; @@ -214,6 +215,41 @@ public static String getSimpleName(String className) { return className; } + /** + * Returns the shortened fully-qualified name for a class represented as a string. + * Shortened name would have package names and owner objects reduced to a single letter. + * For example, {@code com.example.Owner$Inner} would become {@code c.e.O$Inner}. + * IDEs would still be able to recognize these types, but they would take less space + * visually. + * + * @since 4.8.x + * @param typeName The fully-qualified type name + * @return The shortened type name + */ + @Experimental + public static String getShortenedName(String typeName) { + int nameStart = typeName.lastIndexOf('$'); + if (nameStart < 0) { + nameStart = typeName.lastIndexOf('.'); + } + if (nameStart < 0) { + nameStart = 0; + } + StringBuilder shortened = new StringBuilder(); + boolean segmentStart = true; + for (int i = 0; i < nameStart; i++) { + char c = typeName.charAt(i); + if (segmentStart) { + shortened.append(c); + segmentStart = false; + } else if (c == '.' || c == '$') { + shortened.append(c); + segmentStart = true; + } + } + return shortened.append(typeName.substring(nameStart)).toString(); + } + /** * Is the given method name a valid setter name. * diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorCircularDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorCircularDependencyFailureSpec.groovy index e7d7d98a38c..9dcec01c617 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorCircularDependencyFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorCircularDependencyFailureSpec.groovy @@ -31,7 +31,7 @@ class ConstructorCircularDependencyFailureSpec extends Specification { ApplicationContext context = ApplicationContext.run() when:"A bean is obtained that has a setter with @Inject" - MyClassB b = context.getBean(MyClassB) + context.getBean(MyClassB) then:"The implementation is injected" def e = thrown(CircularDependencyException) @@ -40,10 +40,10 @@ Failed to inject value for field [propA] of class: io.micronaut.inject.failures. Message: Circular dependency detected Path Taken: -new MyClassB() - \\---> MyClassB.propA - ^ \\---> new MyClassA([MyClassC propC]) - | \\---> new MyClassC([MyClassB propB]) +new i.m.i.f.C$MyClassB() + \\---> i.m.i.f.C$MyClassB#propA + ^ \\---> new i.m.i.f.C$MyClassA([MyClassC propC]) + | \\---> new i.m.i.f.C$MyClassC([MyClassB propB]) | | +--------------+''' @@ -65,11 +65,11 @@ Failed to inject value for field [propA] of class: io.micronaut.inject.failures. Message: Circular dependency detected Path Taken: -new MyClassD(MyClassB propB) - \\---> new MyClassD([MyClassB propB]) - \\---> MyClassB.propA - ^ \\---> new MyClassA([MyClassC propC]) - | \\---> new MyClassC([MyClassB propB]) +new i.m.i.f.C$MyClassD(MyClassB propB) + \\---> new i.m.i.f.C$MyClassD([MyClassB propB]) + \\---> i.m.i.f.C$MyClassB#propA + ^ \\---> new i.m.i.f.C$MyClassA([MyClassC propC]) + | \\---> new i.m.i.f.C$MyClassC([MyClassB propB]) | | +--------------+''' } diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorDependencyFailureSpec.groovy index 0f19de31fda..6a692044dd9 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorDependencyFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorDependencyFailureSpec.groovy @@ -40,8 +40,8 @@ Failed to inject value for parameter [propA] of class: io.micronaut.inject.failu Message: No bean of type [io.micronaut.inject.failures.ConstructorDependencyFailureSpec\$MyClassA] exists.$space Path Taken:$space -new MyClassB(MyClassA propA) -\\---> new MyClassB([MyClassA propA])""" +new i.m.i.f.C\$MyClassB(MyClassA propA) +\\---> new i.m.i.f.C\$MyClassB([MyClassA propA])""" cleanup: context.close() diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorExceptionSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorExceptionSpec.groovy index 0f04e485a15..ac56564f801 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorExceptionSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/ConstructorExceptionSpec.groovy @@ -40,9 +40,9 @@ Error instantiating bean of type [io.micronaut.inject.failures.ConstructorExcep Message: bad Path Taken: -new MyClassB() -\\---> MyClassB.propA - \\---> new MyClassA([MyClassC propC])''' +new i.m.i.f.C$MyClassB() +\\---> i.m.i.f.C$MyClassB#propA + \\---> new i.m.i.f.C$MyClassA([MyClassC propC])''' cleanup: context.close() diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FactoryCircularDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FactoryCircularDependencyFailureSpec.groovy index f506a7f4091..6db5e278e58 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FactoryCircularDependencyFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FactoryCircularDependencyFailureSpec.groovy @@ -40,10 +40,10 @@ Failed to inject value for parameter [stations] of class: io.micronaut.inject.fa Message: Circular dependency detected Path Taken: -new ElectricalGrid(List stations) - \\---> new ElectricalGrid([List stations]) - ^ \\---> ElectricStationFactory.nuclearStation([MeasuringEquipment equipment]) - | \\---> MeasuringEquipment.grid +new i.m.i.f.F$ElectricalGrid(List stations) + \\---> new i.m.i.f.F$ElectricalGrid([List stations]) + ^ \\---> i.m.i.f.F$ElectricStationFactory#nuclearStation([MeasuringEquipment equipment]) + | \\---> i.m.i.f.F$MeasuringEquipment#grid | | +--------------+''' diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FactoryDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FactoryDependencyFailureSpec.groovy new file mode 100644 index 00000000000..844182feb37 --- /dev/null +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FactoryDependencyFailureSpec.groovy @@ -0,0 +1,83 @@ +/* + * Copyright 2017-2019 original 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 + * + * http://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 io.micronaut.inject.failures + +import io.micronaut.context.ApplicationContext +import io.micronaut.context.annotation.Factory +import io.micronaut.context.exceptions.BeanInstantiationException +import io.micronaut.context.exceptions.CircularDependencyException +import spock.lang.Specification + +import javax.inject.Inject +import javax.inject.Singleton + + +class FactoryDependencyFailureSpec extends Specification { + + void "test dependency with factory failure"() { + given: + ApplicationContext context = ApplicationContext.run() + + when:"A bean is obtained that has a setter with @Inject" + context.getBean(ElectricalGrid) + + then:"The implementation is injected" + def e = thrown(BeanInstantiationException) + e.message.normalize() == '''\ +Error instantiating bean of type [io.micronaut.inject.failures.FactoryDependencyFailureSpec$ElectricStation] + +Message: Outdated equipment +Path Taken: +new i.m.i.f.F$ElectricalGrid(List stations) +\\---> new i.m.i.f.F$ElectricalGrid([List stations]) + \\---> i.m.i.f.F$ElectricStationFactory#nuclearStation([MeasuringEquipment equipment])''' + + cleanup: + context.close() + } + + static class ElectricalGrid { + @Inject + ElectricalGrid(List stations) {} + } + + static class ElectricStation { + ElectricStation() {} + } + + @Factory + static class ElectricStationFactory { + + @Singleton + ElectricStation solarStation() { + return new ElectricStation() + } + + @Singleton + ElectricStation nuclearStation(MeasuringEquipment equipment) { + return new ElectricStation() + } + + } + + @Singleton + static class MeasuringEquipment { + MeasuringEquipment() { + throw new RuntimeException("Outdated equipment") + } + } +} + diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldCircularDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldCircularDependencyFailureSpec.groovy index e491dd8aab7..a50e3238fcf 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldCircularDependencyFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldCircularDependencyFailureSpec.groovy @@ -31,35 +31,35 @@ class FieldCircularDependencyFailureSpec extends Specification { ApplicationContext context = ApplicationContext.run() when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + MyClassB b = context.getBean(MyClassB) then:"The implementation is injected" def e = thrown(CircularDependencyException) e.message.normalize() == '''\ -Failed to inject value for field [a] of class: io.micronaut.inject.failures.FieldCircularDependencyFailureSpec$B +Failed to inject value for field [propA] of class: io.micronaut.inject.failures.FieldCircularDependencyFailureSpec$MyClassB Message: Circular dependency detected Path Taken: -new B() - \\---> B.a - ^ \\---> new A([C c]) - | \\---> C.b +new i.m.i.f.F$MyClassB() + \\---> i.m.i.f.F$MyClassB#propA + ^ \\---> new i.m.i.f.F$MyClassA([MyClassC propC]) + | \\---> i.m.i.f.F$MyClassC#propB | | +--------------+''' cleanup: context.close() } - static class C { - @Inject protected B b + static class MyClassC { + @Inject protected MyClassB propB } @Singleton - static class A { - A(C c) {} + static class MyClassA { + MyClassA(MyClassC propC) {} } @Singleton - static class B { - @Inject protected A a + static class MyClassB { + @Inject protected MyClassA propA } } diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldDependencyMissingFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldDependencyMissingFailureSpec.groovy index 21b10f84457..cac965ec8a4 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldDependencyMissingFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/FieldDependencyMissingFailureSpec.groovy @@ -40,8 +40,8 @@ Failed to inject value for field [propA] of class: io.micronaut.inject.failures. Message: No bean of type [io.micronaut.inject.failures.FieldDependencyMissingFailureSpec\$MyClassA] exists.$space Path Taken:$space -new MyClassB() -\\---> MyClassB.propA""" +new i.m.i.f.F\$MyClassB() +\\---> i.m.i.f.F\$MyClassB#propA""" cleanup: context.close() diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/NestedDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/NestedDependencyFailureSpec.groovy index 1da97c45cf4..0fe100aba58 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/NestedDependencyFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/NestedDependencyFailureSpec.groovy @@ -41,10 +41,10 @@ Failed to inject value for parameter [propD] of class: io.micronaut.inject.failu Message: No bean of type [io.micronaut.inject.failures.NestedDependencyFailureSpec\$MyClassD] exists.$space Path Taken:$space -new MyClassB() -\\---> MyClassB.propA - \\---> new MyClassA([MyClassC propC]) - \\---> new MyClassC([MyClassD propD])""" +new i.m.i.f.N\$MyClassB() +\\---> i.m.i.f.N\$MyClassB#propA + \\---> new i.m.i.f.N\$MyClassA([MyClassC propC]) + \\---> new i.m.i.f.N\$MyClassC([MyClassD propD])""" cleanup: context.close() diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PostConstructExceptionSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PostConstructExceptionSpec.groovy index e863f84d2a8..622308c7be4 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PostConstructExceptionSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PostConstructExceptionSpec.groovy @@ -41,7 +41,7 @@ Error instantiating bean of type [io.micronaut.inject.failures.PostConstructExc Message: bad Path Taken: -new MyClassB()''' +new i.m.i.f.P$MyClassB()''' cleanup: context.close() diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyCircularDependencyFailureSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyCircularDependencyFailureSpec.groovy index e96467cb160..d1eab32fe87 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyCircularDependencyFailureSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyCircularDependencyFailureSpec.groovy @@ -30,18 +30,18 @@ class PropertyCircularDependencyFailureSpec extends Specification { ApplicationContext context = ApplicationContext.run() when:"A bean is obtained that has a setter with @Inject" - context.getBean(B) + context.getBean(MyClassB) then:"The implementation is injected" CircularDependencyException e = thrown() e.message == '''\ -Failed to inject value for parameter [a] of method [setA] of class: io.micronaut.inject.failures.PropertyCircularDependencyFailureSpec$B +Failed to inject value for parameter [propA] of method [setPropA] of class: io.micronaut.inject.failures.PropertyCircularDependencyFailureSpec$MyClassB Message: Circular dependency detected Path Taken: -new B() - \\---> B.setA([A a]) - ^ \\---> A.setB([B b]) +new i.m.i.f.P$MyClassB() + \\---> i.m.i.f.P$MyClassB#setPropA([MyClassA propA]) + ^ \\---> i.m.i.f.P$MyClassA#setPropB([MyClassB propB]) | | +--------+''' @@ -50,12 +50,12 @@ new B() } @Singleton - static class A { - @Inject B b + static class MyClassA { + @Inject MyClassB propB } @Singleton - static class B { - @Inject A a + static class MyClassB { + @Inject MyClassA propA } } diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyDependencyMissingSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyDependencyMissingSpec.groovy index bccde7a15a6..45b2085fa3a 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyDependencyMissingSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyDependencyMissingSpec.groovy @@ -40,8 +40,8 @@ Failed to inject value for parameter [propA] of method [setPropA] of class: io.m Message: No bean of type [io.micronaut.inject.failures.PropertyDependencyMissingSpec\$MyClassA] exists.$space Path Taken:$space -new MyClassB() -\\---> MyClassB.setPropA([MyClassA propA])""" +new i.m.i.f.P\$MyClassB() +\\---> i.m.i.f.P\$MyClassB#setPropA([MyClassA propA])""" cleanup: context.close() diff --git a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyExceptionSpec.groovy b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyExceptionSpec.groovy index 68d8461fe62..ca82d36faba 100644 --- a/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyExceptionSpec.groovy +++ b/inject-groovy/src/test/groovy/io/micronaut/inject/failures/PropertyExceptionSpec.groovy @@ -41,8 +41,8 @@ Error instantiating bean of type [io.micronaut.inject.failures.PropertyExceptio Message: bad Path Taken: -new MyClassB() -\\---> MyClassB.propA''' +new i.m.i.f.P$MyClassB() +\\---> i.m.i.f.P$MyClassB#propA''' cleanup: context.close() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorcirculardependency/ConstructorCircularDependencyFailureSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorcirculardependency/ConstructorCircularDependencyFailureSpec.groovy index fc6b947bae9..591f007510b 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorcirculardependency/ConstructorCircularDependencyFailureSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorcirculardependency/ConstructorCircularDependencyFailureSpec.groovy @@ -35,10 +35,10 @@ Failed to inject value for field [propA] of class: io.micronaut.inject.failures. Message: Circular dependency detected Path Taken: -new MyClassB() - \\---> MyClassB.propA - ^ \\---> new MyClassA([MyClassC propC]) - | \\---> new MyClassC([MyClassB propB]) +new i.m.i.f.c.MyClassB() + \\---> i.m.i.f.c.MyClassB#propA + ^ \\---> new i.m.i.f.c.MyClassA([MyClassC propC]) + | \\---> new i.m.i.f.c.MyClassC([MyClassB propB]) | | +--------------+''' } @@ -57,11 +57,11 @@ Failed to inject value for field [propA] of class: io.micronaut.inject.failures. Message: Circular dependency detected Path Taken: -new MyClassD(MyClassB propB) - \\---> new MyClassD([MyClassB propB]) - \\---> MyClassB.propA - ^ \\---> new MyClassA([MyClassC propC]) - | \\---> new MyClassC([MyClassB propB]) +new i.m.i.f.c.MyClassD(MyClassB propB) + \\---> new i.m.i.f.c.MyClassD([MyClassB propB]) + \\---> i.m.i.f.c.MyClassB#propA + ^ \\---> new i.m.i.f.c.MyClassA([MyClassC propC]) + | \\---> new i.m.i.f.c.MyClassC([MyClassB propB]) | | +--------------+''' } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/ConstructorDependencyFailureSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/ConstructorDependencyFailureSpec.groovy index 923eee053fd..8db2a2371d6 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/ConstructorDependencyFailureSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctordependencyfailure/ConstructorDependencyFailureSpec.groovy @@ -36,8 +36,8 @@ Failed to inject value for parameter [propA] of class: io.micronaut.inject.failu Message: No bean of type [io.micronaut.inject.failures.ctordependencyfailure.MyClassA] exists.$space Path Taken:$space -new MyClassB(MyClassA propA) -\\---> new MyClassB([MyClassA propA])""" +new i.m.i.f.c.MyClassB(MyClassA propA) +\\---> new i.m.i.f.c.MyClassB([MyClassA propA])""" cleanup: context.close() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/ConstructorExceptionSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/ConstructorExceptionSpec.groovy index c54277e7369..88a2c39bc64 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/ConstructorExceptionSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/ctorexception/ConstructorExceptionSpec.groovy @@ -36,9 +36,9 @@ Error instantiating bean of type [io.micronaut.inject.failures.ctorexception.My Message: bad Path Taken: -new MyClassB() -\\---> MyClassB.propA - \\---> new MyClassA([MyClassC propC])''' +new i.m.i.f.c.MyClassB() +\\---> i.m.i.f.c.MyClassB#propA + \\---> new i.m.i.f.c.MyClassA([MyClassC propC])''' cleanup: context.close() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/FieldCircularDependencyFailureSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/FieldCircularDependencyFailureSpec.groovy index 3f3384183fc..219189689da 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/FieldCircularDependencyFailureSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/FieldCircularDependencyFailureSpec.groovy @@ -16,8 +16,6 @@ package io.micronaut.inject.failures.fieldcirculardependency import io.micronaut.context.ApplicationContext -import io.micronaut.context.BeanContext -import io.micronaut.context.DefaultBeanContext import io.micronaut.context.exceptions.CircularDependencyException import spock.lang.Specification @@ -28,19 +26,19 @@ class FieldCircularDependencyFailureSpec extends Specification { ApplicationContext context = ApplicationContext.run(["spec.name": getClass().simpleName]) when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + context.getBean(MyClassB) then:"The implementation is injected" def e = thrown(CircularDependencyException) e.message.normalize() == '''\ -Failed to inject value for field [a] of class: io.micronaut.inject.failures.fieldcirculardependency.B +Failed to inject value for field [propA] of class: io.micronaut.inject.failures.fieldcirculardependency.MyClassB Message: Circular dependency detected Path Taken: -new B() - \\---> B.a - ^ \\---> new A([C c]) - | \\---> C.b +new i.m.i.f.f.MyClassB() + \\---> i.m.i.f.f.MyClassB#propA + ^ \\---> new i.m.i.f.f.MyClassA([MyClassC propC]) + | \\---> i.m.i.f.f.MyClassC#propB | | +--------------+''' diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/A.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassA.java similarity index 93% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/A.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassA.java index 1780268d3ff..758d237937d 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/A.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassA.java @@ -21,7 +21,7 @@ @Requires(property = "spec.name", value = "FieldCircularDependencyFailureSpec") @Singleton -public class A { +public class MyClassA { - public A(C c) {} + public MyClassA(MyClassC propC) {} } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/B.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassB.java similarity index 94% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/B.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassB.java index 897805c7f74..7bfaf441e3e 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/B.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassB.java @@ -22,7 +22,7 @@ @Requires(property = "spec.name", value = "FieldCircularDependencyFailureSpec") @Singleton -public class B { +public class MyClassB { @Inject - protected A a; + protected MyClassA propA; } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/C.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassC.java similarity index 92% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/C.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassC.java index a9273e169ad..e30b5443940 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/C.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/fieldcirculardependency/MyClassC.java @@ -17,7 +17,7 @@ import jakarta.inject.Inject; -public class C { +public class MyClassC { @Inject - protected B b; + protected MyClassB propB; } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/FieldDependencyMissingFailureSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/FieldDependencyMissingFailureSpec.groovy index faee15ea15b..325b56d4293 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/FieldDependencyMissingFailureSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/fielddependencymissing/FieldDependencyMissingFailureSpec.groovy @@ -37,8 +37,8 @@ Failed to inject value for field [propA] of class: io.micronaut.inject.failures. Message: No bean of type [io.micronaut.inject.failures.fielddependencymissing.MyClassA] exists.$space Path Taken:$space -new MyClassB() -\\---> MyClassB.propA""" +new i.m.i.f.f.MyClassB() +\\---> i.m.i.f.f.MyClassB#propA""" cleanup: context.close() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/NestedDependencyFailureSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/NestedDependencyFailureSpec.groovy index 6407268f342..98e6083dc50 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/NestedDependencyFailureSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/nesteddependency/NestedDependencyFailureSpec.groovy @@ -37,10 +37,10 @@ Failed to inject value for parameter [propD] of class: io.micronaut.inject.failu Message: No bean of type [io.micronaut.inject.failures.nesteddependency.MyClassD] exists.$space Path Taken:$space -new MyClassB() -\\---> MyClassB.propA - \\---> new MyClassA([MyClassC propC]) - \\---> new MyClassC([MyClassD propD])""" +new i.m.i.f.n.MyClassB() +\\---> i.m.i.f.n.MyClassB#propA + \\---> new i.m.i.f.n.MyClassA([MyClassC propC]) + \\---> new i.m.i.f.n.MyClassC([MyClassD propD])""" cleanup: context.close() diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/A.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/MyClassA.java similarity index 97% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/A.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/MyClassA.java index 279868f6476..6e3cd98cbb8 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/A.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/MyClassA.java @@ -21,6 +21,6 @@ @Requires(property = "spec.name", value = "PostConstructExceptionSpec") @Singleton -public class A { +public class MyClassA { } diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/B.java b/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/MyClassB.java similarity index 83% rename from inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/B.java rename to inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/MyClassB.java index 72191878acc..24ca0e76e5e 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/B.java +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/MyClassB.java @@ -23,22 +23,22 @@ @Requires(property = "spec.name", value = "PostConstructExceptionSpec") @Singleton -public class B { +public class MyClassB { boolean setupComplete = false; boolean injectedFirst = false; @Inject - protected A another; - private A a; + protected MyClassA another; + private MyClassA propA; @Inject - public void setA(A a ) { - this.a = a; + public void setPropA(MyClassA propA) { + this.propA = propA; } - public A getA() { - return a; + public MyClassA getPropA() { + return propA; } @PostConstruct diff --git a/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/PostConstructExceptionSpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/PostConstructExceptionSpec.groovy index 9e1b68851e5..2a7b5ce323c 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/PostConstructExceptionSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/failures/postconstruct/PostConstructExceptionSpec.groovy @@ -16,7 +16,6 @@ package io.micronaut.inject.failures.postconstruct import io.micronaut.context.ApplicationContext -import io.micronaut.context.env.CachedEnvironment import io.micronaut.context.exceptions.BeanInstantiationException import spock.lang.Specification @@ -27,16 +26,16 @@ class PostConstructExceptionSpec extends Specification { ApplicationContext context = ApplicationContext.run(["spec.name": getClass().simpleName]) when:"A bean is obtained that has a setter with @Inject" - B b = context.getBean(B) + context.getBean(MyClassB) then:"The implementation is injected" def e = thrown(BeanInstantiationException) e.message.normalize() == '''\ -Error instantiating bean of type [io.micronaut.inject.failures.postconstruct.B] +Error instantiating bean of type [io.micronaut.inject.failures.postconstruct.MyClassB] Message: bad Path Taken: -new B()''' +new i.m.i.f.p.MyClassB()''' cleanup: context.close() diff --git a/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java b/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java index e8cfe93d65e..2e16c769c9f 100644 --- a/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java +++ b/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java @@ -26,6 +26,7 @@ import io.micronaut.core.annotation.NonNull; import io.micronaut.core.annotation.Nullable; import io.micronaut.core.convert.ArgumentConversionContext; +import io.micronaut.core.naming.NameUtils; import io.micronaut.core.naming.Named; import io.micronaut.core.type.Argument; import io.micronaut.core.type.ArgumentCoercible; @@ -561,9 +562,9 @@ public String toString() { StringBuilder baseString; if (CONSTRUCTOR_METHOD_NAME.equals(methodName)) { baseString = new StringBuilder("new "); - baseString.append(getDeclaringType().getBeanType().getSimpleName()); + baseString.append(getTypeName(getDeclaringType().getBeanType())); } else { - baseString = new StringBuilder(getDeclaringType().getBeanType().getSimpleName()).append('.'); + baseString = new StringBuilder(getTypeName(getDeclaringType().getBeanType())).append('#'); baseString.append(methodName); } outputArguments(baseString, arguments); @@ -627,7 +628,7 @@ public String toString() { BeanDefinition declaringBean = getDeclaringBean(); if (declaringBean.hasAnnotation(Factory.class)) { ConstructorInjectionPoint constructor = declaringBean.getConstructor(); - var baseString = new StringBuilder(constructor.getDeclaringBeanType().getSimpleName()).append('.'); + var baseString = new StringBuilder(getTypeName(constructor.getDeclaringBeanType())).append(MEMBER_SEPARATOR); baseString.append(getName()); outputArguments(baseString, getArguments()); return baseString.toString(); @@ -658,7 +659,7 @@ public static class MethodSegment extends AbstractSegment implements @Override public String toString() { - StringBuilder baseString = new StringBuilder(getDeclaringType().getBeanType().getSimpleName()).append('.'); + StringBuilder baseString = new StringBuilder(getTypeName(getDeclaringType().getBeanType())).append(MEMBER_SEPARATOR); baseString.append(getName()); outputArguments(baseString, arguments); return baseString.toString(); @@ -706,7 +707,7 @@ public static final class FieldSegment extends AbstractSegment imple @Override public String toString() { - return getDeclaringType().getBeanType().getSimpleName() + "." + getName(); + return getTypeName(getDeclaringType().getBeanType()) + MEMBER_SEPARATOR + getName(); } @Override @@ -786,6 +787,12 @@ public Qualifier getDeclaringBeanQualifier() { * Abstract class for a Segment. */ protected abstract static class AbstractSegment implements Segment, Named { + + /** + * The separator between a type and its member when printing to user. + */ + protected static final String MEMBER_SEPARATOR = "#"; + private final BeanDefinition declaringComponent; @Nullable private final Qualifier qualifier; @@ -805,6 +812,16 @@ protected abstract static class AbstractSegment implements Segment, this.argument = argument; } + /** + * A common method for retrieving a name for type. The default behavior is to use the shortened type name. + * + * @param type The type + * @return The name to be shown to user + */ + protected String getTypeName(Class type) { + return NameUtils.getShortenedName(type.getName()); + } + @Override public String getName() { return name; From 159bbbd285a5958bc19605c22b788cdc23df58d5 Mon Sep 17 00:00:00 2001 From: Andriy Dmytruk Date: Thu, 5 Dec 2024 14:10:05 -0500 Subject: [PATCH 4/4] Add nullability annotations --- .../io/micronaut/core/naming/NameUtils.java | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/io/micronaut/core/naming/NameUtils.java b/core/src/main/java/io/micronaut/core/naming/NameUtils.java index 60264538e5b..4c679d7cb87 100644 --- a/core/src/main/java/io/micronaut/core/naming/NameUtils.java +++ b/core/src/main/java/io/micronaut/core/naming/NameUtils.java @@ -18,6 +18,7 @@ import io.micronaut.core.annotation.AccessorsStyle; import io.micronaut.core.annotation.Experimental; import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; import io.micronaut.core.util.ArgumentUtils; import io.micronaut.core.util.StringUtils; @@ -48,7 +49,7 @@ public class NameUtils { * @param name The name * @return True if it is */ - public static boolean isHyphenatedLowerCase(String name) { + public static boolean isHyphenatedLowerCase(@Nullable String name) { if (name == null || name.isEmpty() || !Character.isLetter(name.charAt(0))) { return false; } @@ -68,7 +69,7 @@ public static boolean isHyphenatedLowerCase(String name) { * @param suffixes The suffix to remove * @return The decapitalized name */ - public static String decapitalizeWithoutSuffix(String name, String... suffixes) { + public static @NonNull String decapitalizeWithoutSuffix(@NonNull String name, String... suffixes) { String decapitalized = decapitalize(name); return trimSuffix(decapitalized, suffixes); } @@ -80,7 +81,7 @@ public static String decapitalizeWithoutSuffix(String name, String... suffixes) * @param suffixes The suffixes * @return The trimmed string */ - public static String trimSuffix(String string, String... suffixes) { + public static @NonNull String trimSuffix(@NonNull String string, String... suffixes) { if (suffixes != null) { for (String suffix : suffixes) { if (string.endsWith(suffix)) { @@ -97,7 +98,7 @@ public static String trimSuffix(String string, String... suffixes) { * @param name The property name * @return The class name */ - public static String capitalize(String name) { + public static @NonNull String capitalize(@NonNull String name) { final String rest = name.substring(1); // Funky rule so that names like 'pNAME' will still work. @@ -114,7 +115,7 @@ public static String capitalize(String name) { * @param name The name * @return The hyphenated string */ - public static String hyphenate(String name) { + public static @NonNull String hyphenate(@NonNull String name) { return hyphenate(name, true); } @@ -125,7 +126,7 @@ public static String hyphenate(String name) { * @param lowerCase Whether the result should be converted to lower case * @return The hyphenated string */ - public static String hyphenate(String name, boolean lowerCase) { + public static @NonNull String hyphenate(@NonNull String name, boolean lowerCase) { String kebabReplaced = name.replace('_', '-').replace(' ', '-'); if (isHyphenatedLowerCase(name)) { return kebabReplaced; @@ -140,7 +141,7 @@ public static String hyphenate(String name, boolean lowerCase) { * @param name The hyphenated string * @return The camel case form */ - public static String dehyphenate(String name) { + public static @NonNull String dehyphenate(@NonNull String name) { StringBuilder sb = new StringBuilder(name.length()); for (String token : StringUtils.splitOmitEmptyStrings(name, '-')) { if (!token.isEmpty() && Character.isLetter(token.charAt(0))) { @@ -159,7 +160,7 @@ public static String dehyphenate(String name) { * @param className The class name * @return The package name */ - public static String getPackageName(String className) { + public static @NonNull String getPackageName(@NonNull String className) { Matcher matcher = DOT_UPPER.matcher(className); if (matcher.find()) { int position = matcher.start(); @@ -174,7 +175,7 @@ public static String getPackageName(String className) { * @param camelCase The camel case name * @return The underscore separated version */ - public static String underscoreSeparate(String camelCase) { + public static @NonNull String underscoreSeparate(@NonNull String camelCase) { return underscoreSeparate(camelCase, false); } @@ -185,7 +186,7 @@ public static String underscoreSeparate(String camelCase) { * @param lowercase true to lowercase the result * @return The underscore separated version */ - public static String underscoreSeparate(String camelCase, boolean lowercase) { + public static @NonNull String underscoreSeparate(@NonNull String camelCase, boolean lowercase) { return separateCamelCase(camelCase.replace('-', '_'), lowercase, '_'); } @@ -195,7 +196,7 @@ public static String underscoreSeparate(String camelCase, boolean lowercase) { * @param camelCase The camel case name * @return The underscore separated version */ - public static String environmentName(String camelCase) { + public static @NonNull String environmentName(@NonNull String camelCase) { return separateCamelCase(camelCase.replace('-', '_').replace('.', '_'), false, '_') .toUpperCase(Locale.ENGLISH); } @@ -206,7 +207,7 @@ public static String environmentName(String camelCase) { * @param className The class name * @return The simple name of the class */ - public static String getSimpleName(String className) { + public static @NonNull String getSimpleName(@NonNull String className) { Matcher matcher = DOT_UPPER.matcher(className); if (matcher.find()) { int position = matcher.start(); @@ -227,7 +228,7 @@ public static String getSimpleName(String className) { * @return The shortened type name */ @Experimental - public static String getShortenedName(String typeName) { + public static @NonNull String getShortenedName(@NonNull String typeName) { int nameStart = typeName.lastIndexOf('$'); if (nameStart < 0) { nameStart = typeName.lastIndexOf('.'); @@ -256,7 +257,7 @@ public static String getShortenedName(String typeName) { * @param methodName The method name * @return True if it is a valid setter name */ - public static boolean isSetterName(String methodName) { + public static boolean isSetterName(@NonNull String methodName) { return isWriterName(methodName, AccessorsStyle.DEFAULT_WRITE_PREFIX); } @@ -307,7 +308,7 @@ public static boolean isWriterName(@NonNull String methodName, @NonNull String[] * @param setterName The setter * @return The property name */ - public static String getPropertyNameForSetter(String setterName) { + public static @NonNull String getPropertyNameForSetter(@NonNull String setterName) { return getPropertyNameForSetter(setterName, AccessorsStyle.DEFAULT_WRITE_PREFIX); } @@ -387,7 +388,7 @@ public static String getPropertyNameForSetter(String setterName) { * @param methodName The method name * @return True if it is a valid getter name */ - public static boolean isGetterName(String methodName) { + public static boolean isGetterName(@NonNull String methodName) { return isReaderName(methodName, AccessorsStyle.DEFAULT_READ_PREFIX); } @@ -446,7 +447,7 @@ private static boolean isValidCharacterAfterReaderWriterPrefix(char c) { * @param getterName The getter * @return The property name */ - public static String getPropertyNameForGetter(String getterName) { + public static @NonNull String getPropertyNameForGetter(@NonNull String getterName) { return getPropertyNameForGetter(getterName, AccessorsStyle.DEFAULT_READ_PREFIX); } @@ -550,8 +551,8 @@ public static String getterNameFor(@NonNull String propertyName, boolean isBoole return nameFor(isBoolean ? PREFIX_IS : PREFIX_GET, propertyName); } - private static String nameFor(String prefix, @NonNull String propertyName) { - if (prefix.isEmpty()) { + private static @NonNull String nameFor(@Nullable String prefix, @NonNull String propertyName) { + if (StringUtils.isEmpty(prefix)) { return propertyName; } @@ -577,7 +578,7 @@ private static String nameFor(String prefix, @NonNull String propertyName) { * @param name The String to decapitalize * @return The decapitalized version of the String */ - public static String decapitalize(String name) { + public static @Nullable String decapitalize(@Nullable String name) { if (name == null) { return null; } @@ -608,7 +609,7 @@ public static String decapitalize(String name) { return name; } - static String separateCamelCase(String name, boolean lowerCase, char separatorChar) { + static @NonNull String separateCamelCase(@NonNull String name, boolean lowerCase, char separatorChar) { StringBuilder newName = new StringBuilder(name.length() + 4); if (!lowerCase) { boolean first = true; @@ -686,7 +687,7 @@ static String separateCamelCase(String name, boolean lowerCase, char separatorCh * @param filename The name of the file * @return The file extension */ - public static String extension(String filename) { + public static @NonNull String extension(@NonNull String filename) { int extensionPos = filename.lastIndexOf('.'); int lastUnixPos = filename.lastIndexOf('/'); int lastWindowsPos = filename.lastIndexOf('\\'); @@ -705,7 +706,7 @@ public static String extension(String filename) { * @param str The string * @return The new string in camel case */ - public static String camelCase(String str) { + public static @NonNull String camelCase(@NonNull String str) { return camelCase(str, true); } @@ -716,7 +717,7 @@ public static String camelCase(String str) { * @param lowerCaseFirstLetter Whether the first letter is in upper case or lower case * @return The new string in camel case */ - public static String camelCase(String str, boolean lowerCaseFirstLetter) { + public static @NonNull String camelCase(@NonNull String str, boolean lowerCaseFirstLetter) { StringBuilder sb = new StringBuilder(str.length()); for (String s : str.split("[\\s_-]")) { String capitalize = capitalize(s); @@ -736,7 +737,7 @@ public static String camelCase(String str, boolean lowerCaseFirstLetter) { * @param path The path of the file * @return The file name without extension */ - public static String filename(String path) { + public static @NonNull String filename(@NonNull String path) { int extensionPos = path.lastIndexOf('.'); int lastUnixPos = path.lastIndexOf('/'); int lastWindowsPos = path.lastIndexOf('\\'); @@ -755,7 +756,7 @@ public static String filename(String path) { * @param str The string to check * @return Whether is valid kebab-case or not */ - public static boolean isValidHyphenatedPropertyName(String str) { + public static boolean isValidHyphenatedPropertyName(@NonNull String str) { return KEBAB_CASE_SEQUENCE.matcher(str).matches(); } @@ -765,7 +766,7 @@ public static boolean isValidHyphenatedPropertyName(String str) { * @param str The string to check * @return Whether is valid environment-style property name or not */ - public static boolean isEnvironmentName(String str) { + public static boolean isEnvironmentName(@NonNull String str) { return ENVIRONMENT_VAR_SEQUENCE.matcher(str).matches(); }