Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement @Attribute Injection. #5547

Merged
merged 41 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
eac6431
skeleton code for @Attribute injection.
chickenchickenlove Mar 29, 2024
2801cdd
Implement Attribute Injection : Apply review
chickenchickenlove Apr 1, 2024
889d887
make test code for Attribute injection.
chickenchickenlove Apr 1, 2024
94a93af
remove duplicate code from AnnotatedValueResolverTest
chickenchickenlove Apr 1, 2024
42ef9a2
Implement Attribute Injection : Apply review2
chickenchickenlove Apr 2, 2024
ab8a08a
Implement Attribute Injection : Apply review3
chickenchickenlove Apr 3, 2024
3fac84e
Implement Attribute Injection : Apply review3. remove useless comments.
chickenchickenlove Apr 3, 2024
f3f8b74
Fix lint, Apply review
chickenchickenlove Apr 3, 2024
821e18f
Add user docs
chickenchickenlove Apr 3, 2024
dce9520
Update core/src/main/java/com/linecorp/armeria/server/annotation/Attr…
chickenchickenlove Apr 5, 2024
fc5771a
Update core/src/main/java/com/linecorp/armeria/server/annotation/Attr…
chickenchickenlove Apr 5, 2024
f017563
Update core/src/main/java/com/linecorp/armeria/server/annotation/Attr…
chickenchickenlove Apr 5, 2024
1927e46
Update core/src/main/java/com/linecorp/armeria/server/annotation/Attr…
chickenchickenlove Apr 5, 2024
ee7a466
Use @default.class and fix link of java docs.
chickenchickenlove Apr 5, 2024
65868b7
Merge branch 'main' into 240329-try1
chickenchickenlove Apr 5, 2024
1d0b6c7
fix lint error
chickenchickenlove Apr 5, 2024
1877117
Update site/src/pages/docs/server-annotated-service.mdx
chickenchickenlove Apr 9, 2024
bdb0088
Update site/src/pages/docs/server-annotated-service.mdx
chickenchickenlove Apr 9, 2024
29543f9
Apply comment to server-annotated-service docs.
chickenchickenlove Apr 9, 2024
957378f
Update core/src/main/java/com/linecorp/armeria/server/annotation/Attr…
chickenchickenlove Apr 12, 2024
2893d3d
modify misstypo
chickenchickenlove Apr 12, 2024
c70c2fb
remove findName().
chickenchickenlove Apr 12, 2024
c1de466
Throw ClassCastException on AttributeResolver.
chickenchickenlove Apr 12, 2024
e6632fa
modify miss typoe
chickenchickenlove Apr 17, 2024
065e43a
Apply code review
chickenchickenlove Apr 17, 2024
e80075f
apply code review
chickenchickenlove Apr 18, 2024
5874745
apply review
chickenchickenlove Apr 18, 2024
c6519ab
apply review
chickenchickenlove Apr 18, 2024
bb087a7
apply review
chickenchickenlove Apr 18, 2024
4560943
apply review
chickenchickenlove Apr 22, 2024
0dd3e77
Update core/src/main/java/com/linecorp/armeria/server/annotation/Attr…
chickenchickenlove Apr 23, 2024
65bc886
throw immedetely if valued is failed to be cast.
chickenchickenlove Apr 23, 2024
c27629b
Update core/src/main/java/com/linecorp/armeria/internal/server/annota…
chickenchickenlove Apr 24, 2024
3943f70
Update core/src/main/java/com/linecorp/armeria/internal/server/annota…
chickenchickenlove Apr 24, 2024
3df348b
Update core/src/test/java/com/linecorp/armeria/internal/server/annota…
chickenchickenlove Apr 24, 2024
7230534
apply review
chickenchickenlove Apr 24, 2024
72d26b2
fix lint error.
chickenchickenlove Apr 24, 2024
8f3631c
Update core/src/main/java/com/linecorp/armeria/server/annotation/Attr…
chickenchickenlove May 2, 2024
9013388
apply review
chickenchickenlove May 2, 2024
588c8bb
Merge branch 'main' into 240329-try1
chickenchickenlove May 7, 2024
70d76cb
fix lint error
chickenchickenlove May 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,51 +20,34 @@

import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.function.Function;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
import com.google.common.base.CaseFormat;

import com.linecorp.armeria.server.annotation.Attribute;
import com.linecorp.armeria.server.annotation.Header;
import com.linecorp.armeria.server.annotation.Param;

final class AnnotatedElementNameUtil {

/**
* Returns the value of the {@link Param} annotation which is specified on the {@code element} if
* the value is not blank. If the value is blank, it returns the name of the specified
* {@code nameRetrievalTarget} object which is an instance of {@link Parameter} or {@link Field}.
*/
static String findName(Param param, Object nameRetrievalTarget) {
requireNonNull(nameRetrievalTarget, "nameRetrievalTarget");

final String value = param.value();
if (DefaultValues.isSpecified(value)) {
checkArgument(!value.isEmpty(), "value is empty.");
return value;
}
return getName(nameRetrievalTarget);
static String findName(Object nameRetrievalTarget, String value) {
return findName(nameRetrievalTarget, value, Function.identity());
}

/**
* Returns the value of the {@link Header} annotation which is specified on the {@code element} if
* the value is not blank. If the value is blank, it returns the name of the specified
* {@code nameRetrievalTarget} object which is an instance of {@link Parameter} or {@link Field}.
*
* <p>Note that the name of the specified {@code nameRetrievalTarget} will be converted as
* {@link CaseFormat#LOWER_HYPHEN} that the string elements are separated with one hyphen({@code -})
* character. The value of the {@link Header} annotation will not be converted because it is clearly
* specified by a user.
* Returns the value of {@link Header}, {@link Param}, {@link Attribute} if the value is not blank.
* If the value is blank, it returns the name of the specified {@code nameRetrievalTarget} object
* which is an instance of {@link Header}, {@link Param}, {@link Attribute} or {@link Field}.
*/
static String findName(Header header, Object nameRetrievalTarget) {
static String findName(Object nameRetrievalTarget, String value, Function<String, String> delegate) {
requireNonNull(nameRetrievalTarget, "nameRetrievalTarget");

final String value = header.value();
if (DefaultValues.isSpecified(value)) {
checkArgument(!value.isEmpty(), "value is empty.");
return value;
}
return toHeaderName(getName(nameRetrievalTarget));
return delegate.apply(getName(nameRetrievalTarget));
}

/**
Expand All @@ -89,8 +72,7 @@ static String getName(Object element) {
throw new IllegalArgumentException(
"cannot obtain the name of the parameter or field automatically. " +
"Please make sure you compiled your code with '-parameters' option. " +
"If not, you need to specify parameter and header names with @" +
Param.class.getSimpleName() + " and @" + Header.class.getSimpleName() + '.');
"Alternatively, you could specify the name explicitly in the annotation.");
}
return parameter.getName();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.findName;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.getName;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.getNameOrDefault;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedElementNameUtil.toHeaderName;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedServiceFactory.findDescription;
import static com.linecorp.armeria.internal.server.annotation.AnnotatedServiceTypeUtil.stringToType;
import static com.linecorp.armeria.internal.server.annotation.DefaultValues.getSpecifiedValue;
Expand Down Expand Up @@ -64,6 +65,7 @@
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.primitives.Primitives;

import com.linecorp.armeria.common.AggregatedHttpRequest;
import com.linecorp.armeria.common.Cookie;
Expand All @@ -84,6 +86,7 @@
import com.linecorp.armeria.internal.server.FileAggregatedMultipart;
import com.linecorp.armeria.internal.server.annotation.AnnotatedBeanFactoryRegistry.BeanFactoryId;
import com.linecorp.armeria.server.ServiceRequestContext;
import com.linecorp.armeria.server.annotation.Attribute;
import com.linecorp.armeria.server.annotation.ByteArrayRequestConverterFunction;
import com.linecorp.armeria.server.annotation.Default;
import com.linecorp.armeria.server.annotation.Delimiter;
Expand All @@ -99,6 +102,7 @@
import com.linecorp.armeria.server.docs.DescriptionInfo;

import io.netty.handler.codec.http.HttpConstants;
import io.netty.util.AttributeKey;
import scala.concurrent.ExecutionContext;

final class AnnotatedValueResolver {
Expand Down Expand Up @@ -444,9 +448,16 @@ private static AnnotatedValueResolver of(AnnotatedElement annotatedElement,
requireNonNull(dependencyInjector, "dependencyInjector");

final DescriptionInfo description = findDescription(annotatedElement);

final Attribute attr = annotatedElement.getAnnotation(Attribute.class);
if (attr != null) {
final String name = findName(typeElement, attr.value());
return ofAttribute(name, attr, annotatedElement, typeElement, type, description);
}

final Param param = annotatedElement.getAnnotation(Param.class);
if (param != null) {
final String name = findName(param, typeElement);
final String name = findName(typeElement, param.value());
if (type == File.class || type == Path.class || type == MultipartFile.class) {
return ofFileParam(name, annotatedElement, typeElement, type, description);
}
Expand All @@ -459,7 +470,7 @@ private static AnnotatedValueResolver of(AnnotatedElement annotatedElement,

final Header header = annotatedElement.getAnnotation(Header.class);
if (header != null) {
final String name = findName(header, typeElement);
final String name = findName(typeElement, header.value(), v -> toHeaderName(v));
ikhoon marked this conversation as resolved.
Show resolved Hide resolved
return ofHeader(name, annotatedElement, typeElement, type, description);
}

Expand Down Expand Up @@ -520,6 +531,7 @@ static List<RequestObjectResolver> addToFirstIfExists(List<RequestObjectResolver

private static boolean isAnnotationPresent(AnnotatedElement element) {
return element.isAnnotationPresent(Param.class) ||
element.isAnnotationPresent(Attribute.class) ||
element.isAnnotationPresent(Header.class) ||
element.isAnnotationPresent(RequestObject.class);
}
Expand Down Expand Up @@ -621,6 +633,35 @@ private static AnnotatedValueResolver ofRequestObject(String name, AnnotatedElem
.build();
}

private static AnnotatedValueResolver ofAttribute(String name,
Attribute attr,
AnnotatedElement annotatedElement,
AnnotatedElement typeElement, Class<?> type,
DescriptionInfo description) {

final ImmutableList.Builder<AttributeKey<?>> builder = ImmutableList.builder();

if (attr.prefix() != DefaultValues.class) {
builder.add(AttributeKey.valueOf(attr.prefix(), name));
}
else {
final Class<?> serviceClass = ((Parameter) annotatedElement).getDeclaringExecutable()
.getDeclaringClass();
builder.add(AttributeKey.valueOf(serviceClass, name));
builder.add(AttributeKey.valueOf(name));
}

final ImmutableList<AttributeKey<?>> attrKeys = builder.build();
return new Builder(annotatedElement, type, name)
.annotationType(Attribute.class)
.typeElement(typeElement)
.supportDefault(true)
.supportContainer(true)
.description(description)
.resolver(attributeResolver(attrKeys))
.build();
}

@Nullable
private static AnnotatedValueResolver ofInjectableTypes(String name, AnnotatedElement annotatedElement,
Class<?> type, boolean useBlockingExecutor) {
Expand Down Expand Up @@ -775,6 +816,23 @@ private static AnnotatedValueResolver ofInjectableTypes0(String name, AnnotatedE
};
}

/**
* Returns a collection value resolver which retrieves a list of string from the specified {@code getter}
* AttributeKeys {@code ofAttribute} and adds them to the specified collection data type.
*/
chickenchickenlove marked this conversation as resolved.
Show resolved Hide resolved
private static BiFunction<AnnotatedValueResolver, ResolverContext, Object>
attributeResolver(Iterable<AttributeKey<?>> attrKeys) {
return (resolver, ctx) -> {
for (AttributeKey<?> attrKey : attrKeys) {
final Object value = ctx.context.attr(attrKey);
if (value != null) {
return value;
}
}
return resolver.defaultOrException();
};
}

/**
* Returns a bean resolver which retrieves a value using request converters. If the target element
* is an annotated bean, a bean factory of the specified {@link BeanFactoryId} will be used for creating an
Expand Down Expand Up @@ -1089,6 +1147,7 @@ private Builder(AnnotatedElement annotatedElement, Type type, String name) {
*/
private Builder annotationType(Class<? extends Annotation> annotationType) {
assert annotationType == Param.class ||
annotationType == Attribute.class ||
annotationType == Header.class ||
annotationType == RequestObject.class : annotationType.getSimpleName();
this.annotationType = annotationType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.linecorp.armeria.server.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.linecorp.armeria.internal.server.annotation.DefaultValues;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Attribute {
chickenchickenlove marked this conversation as resolved.
Show resolved Hide resolved

String value();
chickenchickenlove marked this conversation as resolved.
Show resolved Hide resolved
Class<?> prefix() default DefaultValues.class;
}
chickenchickenlove marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading