From ca1b852483047a483791b53f1b36d9cba321093c Mon Sep 17 00:00:00 2001 From: altro3 Date: Tue, 10 Sep 2024 18:00:12 +0700 Subject: [PATCH 1/7] Fix field generics of inner class (#11173) * Reproducer for #11162 * Fix field generics of inner class --------- Co-authored-by: Denis Stepanov --- .../processing/visitor/JavaFieldElement.java | 5 +- .../visitors/ClassElementSpec.groovy | 105 +++++++++++++++++- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/inject-java/src/main/java/io/micronaut/annotation/processing/visitor/JavaFieldElement.java b/inject-java/src/main/java/io/micronaut/annotation/processing/visitor/JavaFieldElement.java index 9c2ce27976e..9bcc5fe725c 100644 --- a/inject-java/src/main/java/io/micronaut/annotation/processing/visitor/JavaFieldElement.java +++ b/inject-java/src/main/java/io/micronaut/annotation/processing/visitor/JavaFieldElement.java @@ -22,6 +22,7 @@ import io.micronaut.inject.ast.FieldElement; import io.micronaut.inject.ast.MemberElement; import io.micronaut.inject.ast.annotation.ElementAnnotationMetadataFactory; +import io.micronaut.inject.processing.JavaModelUtils; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; @@ -130,8 +131,8 @@ public ClassElement getDeclaringType() { if (resolvedDeclaringClass == null) { Element enclosingElement = variableElement.getEnclosingElement(); if (enclosingElement instanceof TypeElement te) { - String typeName = te.getQualifiedName().toString(); - if (owningType.getName().equals(typeName)) { + String typeName = JavaModelUtils.getClassName(te); + if (owningType.getName().equals(JavaModelUtils.getClassName(te))) { resolvedDeclaringClass = owningType; } else { TypeMirror returnType = te.asType(); diff --git a/inject-java/src/test/groovy/io/micronaut/visitors/ClassElementSpec.groovy b/inject-java/src/test/groovy/io/micronaut/visitors/ClassElementSpec.groovy index 71c2ceca91f..a528e7bddec 100644 --- a/inject-java/src/test/groovy/io/micronaut/visitors/ClassElementSpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/visitors/ClassElementSpec.groovy @@ -15,7 +15,6 @@ */ package io.micronaut.visitors - import io.micronaut.annotation.processing.test.AbstractTypeElementSpec import io.micronaut.annotation.processing.visitor.JavaClassElement import io.micronaut.context.annotation.Replaces @@ -39,14 +38,13 @@ import io.micronaut.inject.ast.WildcardElement import jakarta.validation.Valid import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Null +import java.sql.SQLException +import java.util.function.Supplier import spock.lang.IgnoreIf import spock.lang.Issue import spock.lang.Unroll import spock.util.environment.Jvm -import java.sql.SQLException -import java.util.function.Supplier - class ClassElementSpec extends AbstractTypeElementSpec { void "test package-private methods with broken different package"() { @@ -1181,6 +1179,105 @@ class MyBean {} } + void "test field type with generic in inner class"() { + given: + buildClassElement(""" +package test; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import java.util.List; +import java.util.Locale; + +@Controller("/test") +class TestController { + @Get + public HttpResponse>> endpoint() { + return null; + } + + public static class ResponseObject { + + public T body; + } + + public static class Dto { + + public Locale locale; + } +} +""") { ClassElement ce -> + + def responseType = ce.methods[0].returnType + + responseType.type.name == 'io.micronaut.http.HttpResponse' + assert responseType.typeArguments + assert responseType.typeArguments.size() == 1 + + def typeArg = responseType.firstTypeArgument.orElse(null) + assert typeArg + assert typeArg.fields + assert typeArg.fields.size() == 1 + + def bodyField = typeArg.fields[0] + assert bodyField.name == "body" + assert bodyField.genericType.name == "java.util.List" + assert bodyField.genericType.getFirstTypeArgument().get().name == 'test.TestController$Dto' + } + } + + void "test field type with generic in inner super class"() { + given: + buildClassElement(""" +package test; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import java.util.List; +import java.util.Locale; + +@Controller("/test") +class TestController { + @Get + public HttpResponse>> endpoint() { + return null; + } + + public static class BaseResponseObject { + + public T body; + } + + public static class ResponseObject extends BaseResponseObject { + } + + public static class Dto { + + public Locale locale; + } +} +""") { ClassElement ce -> + + def responseType = ce.methods[0].returnType + + responseType.type.name == 'io.micronaut.http.HttpResponse' + assert responseType.typeArguments + assert responseType.typeArguments.size() == 1 + + def typeArg = responseType.firstTypeArgument.orElse(null) + assert typeArg + assert typeArg.fields + assert typeArg.fields.size() == 1 + + def bodyField = typeArg.fields[0] + assert bodyField.name == "body" + assert bodyField.genericType.name == "java.util.List" + assert bodyField.genericType.getFirstTypeArgument().get().name == 'test.TestController$Dto' + } + } + @Issue('https://github.com/micronaut-projects/micronaut-openapi/issues/593') void 'test declaringType is the implementation and not the interface'() { given: From c180a538064a920c166867a6f51d2baa16496b2d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 14:15:09 +0200 Subject: [PATCH 2/7] chore(deps): update actions/upload-artifact action to v4.4.0 (#11140) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/gradle.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index be653308d2f..909b5fd20df 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -78,7 +78,7 @@ jobs: - name: "📜 Upload binary compatibility check results" if: matrix.java == '17' - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: binary-compatibility-reports path: "**/build/reports/binary-compatibility-*.html" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f4bfd84acbe..bae97dd9153 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -66,13 +66,13 @@ jobs: # Store the hash in a file, which is uploaded as a workflow artifact. sha256sum $ARTIFACTS | base64 -w0 > artifacts-sha256 - name: Upload build artifacts - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: gradle-build-outputs path: build/repo/${{ steps.publish.outputs.group }}/*/${{ steps.publish.outputs.version }}/* retention-days: 5 - name: Upload artifacts-sha256 - uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: artifacts-sha256 path: artifacts-sha256 From 5124a158ab01a81b1110d4a2409b1239385f7b8b Mon Sep 17 00:00:00 2001 From: Graeme Rocher Date: Tue, 17 Sep 2024 10:50:32 -0400 Subject: [PATCH 3/7] Fix broken links in builtInExceptionHandlers.adoc (#11180) --- .../exceptionHandler/builtInExceptionHandlers.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/docs/guide/httpServer/errorHandling/exceptionHandler/builtInExceptionHandlers.adoc b/src/main/docs/guide/httpServer/errorHandling/exceptionHandler/builtInExceptionHandlers.adoc index 1e565c8071b..05fa23287a5 100644 --- a/src/main/docs/guide/httpServer/errorHandling/exceptionHandler/builtInExceptionHandlers.adoc +++ b/src/main/docs/guide/httpServer/errorHandling/exceptionHandler/builtInExceptionHandlers.adoc @@ -12,13 +12,13 @@ The Micronaut framework ships with several built-in handlers: | api:http.server.exceptions.DuplicateRouteHandler[] | api:http.exceptions.HttpStatusException[] | api:http.server.exceptions.HttpStatusHandler[] -| api:http.exceptions.UnsupportedMediaException[] +| api:http.server.exceptions.UnsupportedMediaException[] | api:http.server.exceptions.HttpStatusHandler[] -| api:http.exceptions.NotFoundException[] +| api:http.server.exceptions.NotFoundException[] | api:http.server.exceptions.HttpStatusHandler[] -| api:http.exceptions.NotAcceptableException[] +| api:http.server.exceptions.NotAcceptableException[] | api:http.server.exceptions.HttpStatusHandler[] -| api:http.exceptions.NotAllowedException[] +| api:http.server.exceptions.NotAllowedException[] | api:http.server.exceptions.NotAllowedExceptionHandler[] | `com.fasterxml.jackson.core.JsonProcessingException` | api:http.server.exceptions.JsonExceptionHandler[] From d78a88c358b42fc556982ec71dad44ab9f4ab300 Mon Sep 17 00:00:00 2001 From: Denis Stepanov Date: Fri, 20 Sep 2024 15:54:32 +0200 Subject: [PATCH 4/7] Fix outer injection point of constructor (#11199) --- .../generics/GenericFactorySpec.groovy | 81 ++++++++++++++++++- .../AbstractBeanResolutionContext.java | 5 -- 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/inject-java/src/test/groovy/io/micronaut/inject/factory/generics/GenericFactorySpec.groovy b/inject-java/src/test/groovy/io/micronaut/inject/factory/generics/GenericFactorySpec.groovy index 04ed19fb0b8..03355a885e4 100644 --- a/inject-java/src/test/groovy/io/micronaut/inject/factory/generics/GenericFactorySpec.groovy +++ b/inject-java/src/test/groovy/io/micronaut/inject/factory/generics/GenericFactorySpec.groovy @@ -21,7 +21,7 @@ class SerdeFactory { protected Serde arraySerde() { return new Serde() { }; - } + } } interface Serializer { @@ -58,7 +58,7 @@ class MyBean { @Inject public BaseCache fieldInjectBase; public Cache methodInject; - + @Inject void setCache(Cache methodInject) { this.methodInject = methodInject; @@ -77,7 +77,7 @@ class CacheFactory { Cache buildCache(ArgumentInjectionPoint ip) { Class keyType = ip.asArgument().getTypeVariable("K").get().getType(); Class valueType = ip.asArgument().getTypeVariable("V").get().getType(); - + return new CacheImpl(keyType, valueType); } } @@ -89,7 +89,7 @@ interface Cache extends BaseCache {} class CacheImpl implements Cache { public final Class keyType; public final Class valueType; - + CacheImpl(Class k, Class v) { keyType = k; valueType = v; @@ -117,4 +117,77 @@ class CacheImpl implements Cache { cleanup: context.close() } + + void "test generic factory with type variables - constructor inject"() { + given: + def context = buildContext(''' +package genfact; + +import io.micronaut.context.annotation.*; +import io.micronaut.inject.ArgumentInjectionPoint; +import io.micronaut.inject.ConstructorInjectionPoint; +import jakarta.inject.*; +import io.micronaut.core.type.ArgumentCoercible; + +@Singleton +class MyBean { + public final Cache constructorInject; + MyBean(Cache constructorInject) { + this.constructorInject = constructorInject; + } +} + +@Singleton +class OtherBean { + public Cache invalid; + OtherBean(Cache invalid) { + this.invalid = invalid; + } +} + +@Factory +class CacheFactory { + @Bean + Cache buildCache(ArgumentInjectionPoint ip) { + Class keyType = ip.asArgument().getTypeVariable("K").get().getType(); + Class valueType = ip.asArgument().getTypeVariable("V").get().getType(); + if (ip.getOuterInjectionPoint() instanceof ConstructorInjectionPoint constructorInjectionPoint + && !constructorInjectionPoint.toString().equals("genfact.MyBean(Cache constructorInject)")) { + throw new IllegalStateException(); + } + return new CacheImpl(keyType, valueType); + } +} + +interface BaseCache {} + +interface Cache extends BaseCache {} + +class CacheImpl implements Cache { + public final Class keyType; + public final Class valueType; + + CacheImpl(Class k, Class v) { + keyType = k; + valueType = v; + } +} +''') + when: + def bean = getBean(context, "genfact.MyBean") + + then: + bean.constructorInject.keyType == String + bean.constructorInject.valueType == Integer + + when: + getBean(context, "genfact.OtherBean") + + then: + def e = thrown(DependencyInjectionException) + e.message.contains("No bean of type [genfact.Cache] exists") + + 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 1553a3d479d..959cdbbc1cb 100644 --- a/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java +++ b/inject/src/main/java/io/micronaut/context/AbstractBeanResolutionContext.java @@ -499,11 +499,6 @@ public ConstructorArgumentSegment(BeanDefinition declaringType, Qualifie super(declaringType, qualifier, methodName, argument, arguments); } - @Override - public CallableInjectionPoint getOuterInjectionPoint() { - throw new UnsupportedOperationException("Outer injection point inaccessible from here"); - } - @Override public BeanDefinition getDeclaringBean() { return getDeclaringType(); From e0e378bc3739fa78cbde2aa4b74b67c8b90c2a22 Mon Sep 17 00:00:00 2001 From: Jonas Konrad Date: Mon, 30 Sep 2024 15:48:09 +0200 Subject: [PATCH 5/7] Fix forwarding null when CancellableMonoSink is cancelled (#11221) Might fix #11209, will see. --- .../client/netty/CancellableMonoSink.java | 4 +- .../netty/CancellableMonoSinkSpec.groovy | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 http-client/src/test/groovy/io/micronaut/http/client/netty/CancellableMonoSinkSpec.groovy diff --git a/http-client/src/main/java/io/micronaut/http/client/netty/CancellableMonoSink.java b/http-client/src/main/java/io/micronaut/http/client/netty/CancellableMonoSink.java index c86796a3f19..b7cbeda3f05 100644 --- a/http-client/src/main/java/io/micronaut/http/client/netty/CancellableMonoSink.java +++ b/http-client/src/main/java/io/micronaut/http/client/netty/CancellableMonoSink.java @@ -44,6 +44,7 @@ final class CancellableMonoSink implements Publisher, Sinks.One, Subscr private T value; private Throwable failure; private boolean complete = false; + private boolean cancelled = false; private Subscriber subscriber = null; private boolean subscriberWaiting = false; @@ -72,7 +73,7 @@ public void subscribe(Subscriber s) { } private void tryForward() { - if (subscriberWaiting && complete) { + if (subscriberWaiting && complete && !cancelled) { if (failure == null) { if (value != EMPTY) { subscriber.onNext(value); @@ -181,6 +182,7 @@ public void cancel() { lock.lock(); try { complete = true; + cancelled = true; } finally { lock.unlock(); } diff --git a/http-client/src/test/groovy/io/micronaut/http/client/netty/CancellableMonoSinkSpec.groovy b/http-client/src/test/groovy/io/micronaut/http/client/netty/CancellableMonoSinkSpec.groovy new file mode 100644 index 00000000000..b15f36e27f2 --- /dev/null +++ b/http-client/src/test/groovy/io/micronaut/http/client/netty/CancellableMonoSinkSpec.groovy @@ -0,0 +1,41 @@ +package io.micronaut.http.client.netty + +import org.reactivestreams.Subscriber +import org.reactivestreams.Subscription +import reactor.core.publisher.Mono +import spock.lang.Specification + +class CancellableMonoSinkSpec extends Specification { + def "cancel before request"() { + given: + def sink = new CancellableMonoSink(null) + def result = "unset" + Subscription subscription = null + sink.subscribe(new Subscriber() { + @Override + void onSubscribe(Subscription s) { + subscription = s + } + + @Override + void onNext(String s) { + result = s + } + + @Override + void onError(Throwable t) { + } + + @Override + void onComplete() { + } + }) + + when: + sink.cancel() + subscription.request(1) + + then: + result == "unset" + } +} From 0f5b8be498fc714cdb491a354cb7085b0cfe5225 Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Wed, 2 Oct 2024 14:12:09 +0000 Subject: [PATCH 6/7] [skip ci] Release v4.6.6 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a9b448d8f14..d92787be490 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=4.6.6-SNAPSHOT +projectVersion=4.6.6 projectGroupId=io.micronaut projectDesc=Core components supporting the Micronaut Framework title=Micronaut Core From 12e0ed50dcddb216d075f2cec9db9bf1d8221e21 Mon Sep 17 00:00:00 2001 From: micronaut-build Date: Wed, 2 Oct 2024 14:27:53 +0000 Subject: [PATCH 7/7] chore: Bump version to 4.6.7-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d92787be490..1b0e3369717 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -projectVersion=4.6.6 +projectVersion=4.6.7-SNAPSHOT projectGroupId=io.micronaut projectDesc=Core components supporting the Micronaut Framework title=Micronaut Core