Skip to content

Commit

Permalink
Make AnnotatedService public (#5628)
Browse files Browse the repository at this point in the history
Motivation:

- Related issue: #5382
- It'd be nice if a user can access the information provided by
`AnnotatredService`, but it is not part of the public API.

### Modifications:

- Split `AnnotatedService` into `AnnotatedService` (interface) and 
  `DefaultAnnotatedService` (implementation).

### Result:
- Closes #5382

Before:
```java
        ServiceRequestContext serviceRequestContext = ServiceRequestContext.current();
        AnnotatedService service = serviceRequestContext.config().service().as(AnnotatedService.class);

        // This doesn't compile.
        // service.method(); 
        // service.defaultStatus();
```

After:
```java
        ServiceRequestContext serviceRequestContext = ServiceRequestContext.current();
        AnnotatedService service = serviceRequestContext.config().service().as(AnnotatedService.class);

        // This compiles and provides useful properties.
        service.method(); 
        service.defaultStatus();
```

---------

Co-authored-by: Ikhun Um <ih.pert@gmail.com>
Co-authored-by: Trustin Lee <t@motd.kr>
Co-authored-by: jrhee17 <guins_j@guins.org>
Co-authored-by: minux <songmw725@gmail.com>
  • Loading branch information
5 people authored May 7, 2024
1 parent 7cc27f5 commit 7d562ce
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
package com.linecorp.armeria.internal.common.util;

import com.linecorp.armeria.common.util.Unwrappable;
import com.linecorp.armeria.internal.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.HttpService;
import com.linecorp.armeria.server.annotation.AnnotatedService;

public final class ServiceNamingUtil {

Expand All @@ -32,7 +32,12 @@ public static String fullTypeHttpServiceName(HttpService service) {
continue;
}
if (delegate instanceof AnnotatedService) {
return ((AnnotatedService) delegate).serviceName();
final AnnotatedService annotatedService = (AnnotatedService) delegate;
final String serviceName = annotatedService.name();
if (serviceName != null) {
return serviceName;
}
return annotatedService.serviceClass().getName();
} else {
return delegate.getClass().getName();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import com.linecorp.armeria.server.RoutePathType;
import com.linecorp.armeria.server.Service;
import com.linecorp.armeria.server.ServiceConfig;
import com.linecorp.armeria.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.Header;
import com.linecorp.armeria.server.annotation.Param;
import com.linecorp.armeria.server.annotation.RequestObject;
Expand Down Expand Up @@ -142,11 +143,11 @@ public ServiceSpecification generateSpecification(Set<ServiceConfig> serviceConf
final Map<Class<?>, Set<MethodInfo>> methodInfos = new HashMap<>();
final Map<Class<?>, DescriptionInfo> serviceDescription = new HashMap<>();
serviceConfigs.forEach(sc -> {
final AnnotatedService service = sc.service().as(AnnotatedService.class);
final DefaultAnnotatedService service = sc.service().as(DefaultAnnotatedService.class);
if (service != null) {
final Class<?> serviceClass = ClassUtil.getUserClass(service.object().getClass());
final Class<?> serviceClass = service.serviceClass();
final String className = serviceClass.getName();
final String methodName = service.method().getName();
final String methodName = service.methodName();
if (!filter.test(name(), className, methodName)) {
return;
}
Expand All @@ -164,8 +165,9 @@ private static void addServiceDescription(Map<Class<?>, DescriptionInfo> service
}

private static void addMethodInfo(Map<Class<?>, Set<MethodInfo>> methodInfos,
String hostnamePattern, AnnotatedService service,
String hostnamePattern, DefaultAnnotatedService service,
Class<?> serviceClass) {

final Route route = service.route();
final EndpointInfo endpoint = endpointInfo(route, hostnamePattern);
final Method method = service.method();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import com.linecorp.armeria.server.HttpService;
import com.linecorp.armeria.server.Route;
import com.linecorp.armeria.server.annotation.AnnotatedService;

/**
* Details of an annotated HTTP service method.
Expand All @@ -32,12 +33,12 @@ public final class AnnotatedServiceElement {

private final Route route;

private final AnnotatedService service;
private final DefaultAnnotatedService service;

private final Function<? super HttpService, ? extends HttpService> decorator;

AnnotatedServiceElement(Route route,
AnnotatedService service,
DefaultAnnotatedService service,
Function<? super HttpService, ? extends HttpService> decorator) {
this.route = requireNonNull(route, "route");
this.service = requireNonNull(service, "service");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import com.linecorp.armeria.server.Route;
import com.linecorp.armeria.server.annotation.AdditionalHeader;
import com.linecorp.armeria.server.annotation.AdditionalTrailer;
import com.linecorp.armeria.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.Blocking;
import com.linecorp.armeria.server.annotation.Consumes;
import com.linecorp.armeria.server.annotation.Decorator;
Expand Down Expand Up @@ -291,8 +292,10 @@ static List<AnnotatedServiceElement> create(String pathPrefix, Object object, Me
queryDelimiter);
return new AnnotatedServiceElement(
route,
new AnnotatedService(object, method, overloadId, resolvers, eh, res, route, defaultStatus,
responseHeaders, responseTrailers, needToUseBlockingTaskExecutor),
new DefaultAnnotatedService(object, method, overloadId,
resolvers, eh, res, route, defaultStatus,
responseHeaders, responseTrailers,
needToUseBlockingTaskExecutor),
decorator(method, clazz, dependencyInjector));
}).collect(toImmutableList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,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.AnnotatedService;
import com.linecorp.armeria.server.annotation.ByteArrayRequestConverterFunction;
import com.linecorp.armeria.server.annotation.Default;
import com.linecorp.armeria.server.annotation.Delimiter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import com.linecorp.armeria.server.RoutingContext;
import com.linecorp.armeria.server.ServiceRequestContext;
import com.linecorp.armeria.server.SimpleDecoratingHttpService;
import com.linecorp.armeria.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.ExceptionHandlerFunction;
import com.linecorp.armeria.server.annotation.ExceptionVerbosity;
import com.linecorp.armeria.server.annotation.FallthroughException;
Expand All @@ -76,15 +77,16 @@
* This class is not supposed to be instantiated by a user. Please check out the documentation
* <a href="https://armeria.dev/docs/server-annotated-service">Annotated HTTP Service</a> to use this.
*/
public final class AnnotatedService implements HttpService {
private static final Logger logger = LoggerFactory.getLogger(AnnotatedService.class);
final class DefaultAnnotatedService implements AnnotatedService {
private static final Logger logger = LoggerFactory.getLogger(DefaultAnnotatedService.class);

private static final MethodHandles.Lookup lookup = MethodHandles.lookup();

private static final CompletableFuture<AggregatedResult>
NO_AGGREGATION_FUTURE = UnmodifiableFuture.completedFuture(AggregatedResult.EMPTY);

private final Object object;
private final Class<?> serviceClass;
private final Method method;
private final int overloadId;
private final MethodHandle methodHandle;
Expand All @@ -106,22 +108,23 @@ public final class AnnotatedService implements HttpService {

private final ResponseType responseType;
private final boolean useBlockingTaskExecutor;
private final String serviceName;
private final boolean serviceNameSetByAnnotation;

AnnotatedService(Object object, Method method,
int overloadId, List<AnnotatedValueResolver> resolvers,
List<ExceptionHandlerFunction> exceptionHandlers,
List<ResponseConverterFunction> responseConverters,
Route route,
HttpStatus defaultStatus,
HttpHeaders defaultHttpHeaders,
HttpHeaders defaultHttpTrailers,
boolean useBlockingTaskExecutor) {
@Nullable
private final String name;

DefaultAnnotatedService(Object object, Method method,
int overloadId, List<AnnotatedValueResolver> resolvers,
List<ExceptionHandlerFunction> exceptionHandlers,
List<ResponseConverterFunction> responseConverters,
Route route,
HttpStatus defaultStatus,
HttpHeaders defaultHttpHeaders,
HttpHeaders defaultHttpTrailers,
boolean useBlockingTaskExecutor) {
this.object = requireNonNull(object, "object");
this.method = requireNonNull(method, "method");
checkArgument(overloadId >= 0, "overloadId: %s (expected: >= 0)", overloadId);
this.overloadId = overloadId;
serviceClass = ClassUtil.getUserClass(object.getClass());

checkArgument(!method.isVarArgs(), "%s#%s declared to take a variable number of arguments",
method.getDeclaringClass().getSimpleName(), method.getName());
Expand Down Expand Up @@ -165,11 +168,9 @@ public final class AnnotatedService implements HttpService {
serviceName = AnnotationUtil.findFirst(object.getClass(), ServiceName.class);
}
if (serviceName != null) {
this.serviceName = serviceName.value();
serviceNameSetByAnnotation = true;
name = serviceName.value();
} else {
this.serviceName = ClassUtil.getUserClass(object.getClass()).getName();
serviceNameSetByAnnotation = false;
name = null;
}

this.method.setAccessible(true);
Expand Down Expand Up @@ -220,23 +221,23 @@ private static void warnIfHttpResponseArgumentExists(Type returnType,
}
}

public String serviceName() {
return serviceName;
}

public boolean serviceNameSetByAnnotation() {
return serviceNameSetByAnnotation;
@Override
public String name() {
return name;
}

public String methodName() {
return method.getName();
@Override
public Object serviceObject() {
return object;
}

Object object() {
return object;
@Override
public Class<?> serviceClass() {
return serviceClass;
}

Method method() {
@Override
public Method method() {
return method;
}

Expand All @@ -248,12 +249,13 @@ List<AnnotatedValueResolver> annotatedValueResolvers() {
return resolvers;
}

Route route() {
@Override
public Route route() {
return route;
}

// TODO: Expose through `AnnotatedServiceConfig`, see #5382.
HttpStatus defaultStatus() {
@Override
public HttpStatus defaultStatus() {
return defaultStatus;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.linecorp.armeria.common.ResponseHeaders;
import com.linecorp.armeria.common.ResponseHeadersBuilder;
import com.linecorp.armeria.server.ServiceRequestContext;
import com.linecorp.armeria.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.HttpResult;

final class HttpResultUtil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import com.linecorp.armeria.common.SuccessFunction;
import com.linecorp.armeria.common.annotation.UnstableApi;
import com.linecorp.armeria.common.util.BlockingTaskExecutor;
import com.linecorp.armeria.internal.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.ExceptionHandlerFunction;
import com.linecorp.armeria.server.annotation.RequestConverterFunction;
import com.linecorp.armeria.server.annotation.ResponseConverterFunction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.util.Exceptions;
import com.linecorp.armeria.internal.common.util.TemporaryThreadLocals;
import com.linecorp.armeria.internal.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.AnnotatedService;

/**
* The default {@link ServerErrorHandler} that is used when a user didn't specify one.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.util.BlockingTaskExecutor;
import com.linecorp.armeria.common.util.EventLoopGroups;
import com.linecorp.armeria.internal.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.logging.AccessLogWriter;

import io.netty.channel.EventLoopGroup;
Expand Down Expand Up @@ -338,8 +338,11 @@ ServiceConfigBuilder toServiceConfigBuilder(Route route, String contextPath, Htt
} else {
// Set the default service name only when the service name is set using @ServiceName.
// If it's not, the global defaultServiceNaming is used.
if (annotatedService != null && annotatedService.serviceNameSetByAnnotation()) {
serviceConfigBuilder.defaultServiceName(annotatedService.serviceName());
if (annotatedService != null) {
final String serviceName = annotatedService.name();
if (serviceName != null) {
serviceConfigBuilder.defaultServiceName(serviceName);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import com.linecorp.armeria.common.SuccessFunction;
import com.linecorp.armeria.common.annotation.UnstableApi;
import com.linecorp.armeria.common.util.BlockingTaskExecutor;
import com.linecorp.armeria.internal.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.ExceptionHandlerFunction;
import com.linecorp.armeria.server.annotation.RequestConverterFunction;
import com.linecorp.armeria.server.annotation.ResponseConverterFunction;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2017 LINE Corporation
*
* LINE Corporation licenses this file to you 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:
*
* https://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 com.linecorp.armeria.server.annotation;

import java.lang.reflect.Method;

import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.server.HttpService;
import com.linecorp.armeria.server.Route;
import com.linecorp.armeria.server.ServerBuilder;

/**
* An {@link HttpService} which is defined by a {@link Path} or HTTP method annotations.
* This class is designed to provide a common interface and expose as public for Annotated HTTP Service.
* Please check out the documentation at
* <a href="https://armeria.dev/docs/server-annotated-service">Annotated HTTP Service</a> to use this.
*/
@Nullable
public interface AnnotatedService extends HttpService {

/**
* Returns the name of this annotated service specified with {@link ServiceName}.
*/
@Nullable
String name();

/**
* Returns the annotated service object specified with {@link ServerBuilder#annotatedService(Object)}.
*/
Object serviceObject();

/**
* Returns the {@link Class} of the annotated service object specified
* with {@link ServerBuilder#annotatedService(Object)}.
*/
Class<?> serviceClass();

/**
* Returns the target {@link Method} invoked when a request is received.
*/
Method method();

/**
* Returns the name of the target method invoked when a request is received.
*/
default String methodName() {
return method().getName();
}

/**
* Returns the {@link Route} for this {@link AnnotatedService}.
*/
Route route();

/**
* Returns the default {@link HttpStatus} specified with {@link StatusCode}.
* If {@link StatusCode} is not given, {@link HttpStatus#OK} is returned by default.
* If the method returns a void type such as {@link Void} or Kotlin Unit, {@link HttpStatus#NO_CONTENT} is
* returned.
*/
HttpStatus defaultStatus();
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import com.linecorp.armeria.common.RequestContext;
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.internal.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.annotation.AnnotatedService;
import com.linecorp.armeria.server.kotlin.CoroutineContextService;

import io.netty.util.AttributeKey;
Expand Down

0 comments on commit 7d562ce

Please sign in to comment.