Skip to content

Commit

Permalink
Merge branch '4.6.x' into 4.7.x
Browse files Browse the repository at this point in the history
  • Loading branch information
sdelamo committed Oct 7, 2024
2 parents 84b0fb2 + 12e0ed5 commit 5e2eb9a
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ final class CancellableMonoSink<T> implements Publisher<T>, Sinks.One<T>, Subscr
private T value;
private Throwable failure;
private boolean complete = false;
private boolean cancelled = false;
private Subscriber<? super T> subscriber = null;
private boolean subscriberWaiting = false;

Expand Down Expand Up @@ -72,7 +73,7 @@ public void subscribe(Subscriber<? super T> s) {
}

private void tryForward() {
if (subscriberWaiting && complete) {
if (subscriberWaiting && complete && !cancelled) {
if (failure == null) {
if (value != EMPTY) {
subscriber.onNext(value);
Expand Down Expand Up @@ -181,6 +182,7 @@ public void cancel() {
lock.lock();
try {
complete = true;
cancelled = true;
} finally {
lock.unlock();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String>(null)
def result = "unset"
Subscription subscription = null
sink.subscribe(new Subscriber<String>() {
@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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class SerdeFactory {
protected <T> Serde<T[]> arraySerde() {
return new Serde<T[]>() {
};
}
}
}
interface Serializer<T> {
Expand Down Expand Up @@ -58,7 +58,7 @@ class MyBean {
@Inject
public BaseCache<String, Integer> fieldInjectBase;
public Cache<StringBuilder, Float> methodInject;
@Inject
void setCache(Cache<StringBuilder, Float> methodInject) {
this.methodInject = methodInject;
Expand All @@ -77,7 +77,7 @@ class CacheFactory {
<K extends CharSequence, V> Cache<K, V> buildCache(ArgumentInjectionPoint<?, ?> ip) {
Class<?> keyType = ip.asArgument().getTypeVariable("K").get().getType();
Class<?> valueType = ip.asArgument().getTypeVariable("V").get().getType();
return new CacheImpl(keyType, valueType);
}
}
Expand All @@ -89,7 +89,7 @@ interface Cache<K, V> extends BaseCache<K, V> {}
class CacheImpl implements Cache {
public final Class<?> keyType;
public final Class<?> valueType;
CacheImpl(Class<?> k, Class<?> v) {
keyType = k;
valueType = v;
Expand Down Expand Up @@ -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<String, Integer> constructorInject;
MyBean(Cache<String, Integer> constructorInject) {
this.constructorInject = constructorInject;
}
}
@Singleton
class OtherBean {
public Cache<Boolean, Integer> invalid;
OtherBean(Cache<Boolean, Integer> invalid) {
this.invalid = invalid;
}
}
@Factory
class CacheFactory {
@Bean
<K extends CharSequence, V> Cache<K, V> 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<String K, Integer V> constructorInject)")) {
throw new IllegalStateException();
}
return new CacheImpl(keyType, valueType);
}
}
interface BaseCache<K, V> {}
interface Cache<K, V> extends BaseCache<K, V> {}
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<java.lang.Boolean,java.lang.Integer>] exists")

cleanup:
context.close()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"() {
Expand Down Expand Up @@ -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<ResponseObject<List<Dto>>> endpoint() {
return null;
}
public static class ResponseObject<T> {
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<ResponseObject<List<Dto>>> endpoint() {
return null;
}
public static class BaseResponseObject<T> {
public T body;
}
public static class ResponseObject<T> extends BaseResponseObject<T> {
}
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -499,11 +499,6 @@ public ConstructorArgumentSegment(BeanDefinition<Object> declaringType, Qualifie
super(declaringType, qualifier, methodName, argument, arguments);
}

@Override
public CallableInjectionPoint<Object> getOuterInjectionPoint() {
throw new UnsupportedOperationException("Outer injection point inaccessible from here");
}

@Override
public BeanDefinition<Object> getDeclaringBean() {
return getDeclaringType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
Expand Down

0 comments on commit 5e2eb9a

Please sign in to comment.