AnnotatedService implements HttpService { AnnotatedService(Object object, Method method, List<AnnotatedValueResolver> resolvers, List<ExceptionHandlerFunction> exceptionHandlers, List<ResponseConverterFunction> responseConverters, Route route, ResponseHeaders defaultHttpHeaders, HttpHeaders defaultHttpTrailers, boolean useBlockingTaskExecutor) { this.object = requireNonNull(object, "object"); this.method = requireNonNull(method, "method"); isKotlinSuspendingMethod = KotlinUtil.isSuspendingFunction(method); this.resolvers = requireNonNull(resolvers, "resolvers"); exceptionHandler = new CompositeExceptionHandlerFunction(object.getClass().getSimpleName(), method.getName(), requireNonNull(exceptionHandlers, "exceptionHandlers")); responseConverter = responseConverter( method, requireNonNull(responseConverters, "responseConverters"), exceptionHandler); aggregationStrategy = AggregationStrategy.from(resolvers); this.route = requireNonNull(route, "route"); this.defaultHttpHeaders = requireNonNull(defaultHttpHeaders, "defaultHttpHeaders"); this.defaultHttpTrailers = requireNonNull(defaultHttpTrailers, "defaultHttpTrailers"); this.useBlockingTaskExecutor = useBlockingTaskExecutor; final Class<?> returnType = method.getReturnType(); if (HttpResponse.class.isAssignableFrom(returnType)) { responseType = ResponseType.HTTP_RESPONSE; } else if (CompletionStage.class.isAssignableFrom(returnType)) { responseType = ResponseType.COMPLETION_STAGE; } else if (isKotlinSuspendingMethod) { responseType = ResponseType.KOTLIN_COROUTINES; } else { responseType = ResponseType.OTHER_OBJECTS; } callKotlinSuspendingMethod = KotlinUtil.getCallKotlinSuspendingMethod(); ServiceName serviceName = AnnotationUtil.findFirst(method, ServiceName.class); if (serviceName == null) { serviceName = AnnotationUtil.findFirst(object.getClass(), ServiceName.class); } if (serviceName != null) { defaultServiceName = serviceName.value(); } else { defaultServiceName = object.getClass().getName(); } this.method.setAccessible(true); } AnnotatedService(Object object, Method method,
                     List<AnnotatedValueResolver> resolvers,
                     List<ExceptionHandlerFunction> exceptionHandlers,
                     List<ResponseConverterFunction> responseConverters,
                     Route route,
                     ResponseHeaders defaultHttpHeaders,
                     HttpHeaders defaultHttpTrailers,
                     boolean useBlockingTaskExecutor); String serviceName(); String methodName(); @Override HttpResponse serve(ServiceRequestContext ctx, HttpRequest req); Function<? super HttpService, ? extends HttpService> exceptionHandlingDecorator();  }
@Test void testAnnotatedService() throws Exception { try (CloseableHttpClient hc = HttpClients.createMinimal()) { testBody(hc, get("/1/int/42"), "Integer: 42"); testBody(hc, get("/1/int-async/42"), "Integer: 43"); testBody(hc, post("/1/long/42"), "Number[42]"); testBody(hc, get("/1/string/blah"), "String: blah"); testBody(hc, get("/1/string/%F0%90%8D%88"), "String: \uD800\uDF48", StandardCharsets.UTF_8); testBody(hc, get("/1/path/ctx/async/1"), "String[/1/path/ctx/async/1]"); testBody(hc, get("/1/path/req/async/1"), "String[/1/path/req/async/1]"); testBody(hc, get("/1/path/ctx/sync/1"), "String[/1/path/ctx/sync/1]"); testBody(hc, get("/1/path/req/sync/1"), "String[/1/path/req/sync/1]"); testStatusCode(hc, get("/1/int/forty-two"), 400); testStatusCode(hc, post("/1/long/"), 404); testStatusCode(hc, post("/1/string/blah"), 405); testStatusCode(hc, get("/1/exception/42"), 500); testStatusCode(hc, get("/1/exception-async/1"), 500); testBody(hc, get("/2/int/42"), "Number[42]"); testBody(hc, post("/2/long/42"), "Number[42]"); testBody(hc, get("/2/string/blah"), "String: blah"); testBody(hc, get("/2/boolean/true"), "String[true]"); testBody(hc, get("/2/boolean/1"), "String[true]"); testBody(hc, get("/2/boolean/false"), "String[false]"); testBody(hc, get("/2/boolean/0"), "String[false]"); testStatusCode(hc, get("/2/int/forty-two"), 400); testStatusCode(hc, get("/2/boolean/maybe"), 400); testStatusCode(hc, post("/2/long/"), 404); testStatusCode(hc, post("/2/string/blah"), 405); testBody(hc, get("/2/null1"), "(null)"); testBody(hc, get("/2/null2"), "(null)"); testBody(hc, get("/3/int/42"), "Number[42]"); testBody(hc, get("/3/string/blah"), "String[blah]"); testBody(hc, get("/3/no-path-param"), "String[no-path-param]"); testStatusCode(hc, get("/3/undefined"), 404); } }