From d21cc6e898f374ce5f2fc564d54f71fcaa58e1a6 Mon Sep 17 00:00:00 2001 From: joshua bauer Date: Wed, 19 Dec 2018 15:24:56 -0800 Subject: [PATCH] Improve blocking handler performance. --- .../sinistral/proteus/ProteusApplication.java | 7 +- .../server/handlers/HandlerGenerator.java | 1898 ++++++++--------- .../proteus/services/SwaggerService.java | 6 - src/main/resources/reference.conf | 2 +- src/test/resources/application.conf | 2 +- 5 files changed, 873 insertions(+), 1042 deletions(-) diff --git a/src/main/java/io/sinistral/proteus/ProteusApplication.java b/src/main/java/io/sinistral/proteus/ProteusApplication.java index d6d5cfa..ef82725 100644 --- a/src/main/java/io/sinistral/proteus/ProteusApplication.java +++ b/src/main/java/io/sinistral/proteus/ProteusApplication.java @@ -307,14 +307,15 @@ public void buildServer() .setBufferSize(Long.valueOf(config.getMemorySize("undertow.bufferSize").toBytes()).intValue()) .setIoThreads(Runtime.getRuntime().availableProcessors() * config.getInt("undertow.ioThreadsMultiplier")) + .setWorkerThreads(Runtime.getRuntime().availableProcessors() * config.getInt("undertow.workerThreadMultiplier")) + .setDirectBuffers(config.getBoolean("undertow.directBuffers")) + .setSocketOption(org.xnio.Options.BACKLOG, config.getInt("undertow.socket.backlog")) + .setSocketOption(org.xnio.Options.REUSE_ADDRESSES, config.getBoolean("undertow.socket.reuseAddresses")) .setServerOption(UndertowOptions.ENABLE_HTTP2, config.getBoolean("undertow.server.enableHttp2")) .setServerOption(UndertowOptions.ALWAYS_SET_DATE, config.getBoolean("undertow.server.alwaysSetDate")) - .setSocketOption(org.xnio.Options.BACKLOG, config.getInt("undertow.socket.backlog")) .setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, config.getBoolean("undertow.server.alwaysSetKeepAlive")) .setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, config.getBoolean("undertow.server.recordRequestStartTime")) .setServerOption(UndertowOptions.MAX_ENTITY_SIZE, config.getBytes("undertow.server.maxEntitySize")) - .setSocketOption(org.xnio.Options.REUSE_ADDRESSES, config.getBoolean("undertow.socket.reuseAddresses")) - .setWorkerThreads(config.getInt("undertow.workerThreads")) .setHandler(handler); diff --git a/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java b/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java index eb55153..518a1ca 100644 --- a/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java +++ b/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java @@ -1,5 +1,5 @@ /** - * + * */ package io.sinistral.proteus.server.handlers; @@ -74,87 +74,79 @@ * annotation (i.e. javax.ws.rs.GET) * @author jbauer */ -public class HandlerGenerator -{ - - static Logger log = LoggerFactory.getLogger(HandlerGenerator.class.getCanonicalName()); - - private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("(java\\.util\\.[A-Za-z]+)<([^>]+)", Pattern.DOTALL | Pattern.UNIX_LINES); - private static final Pattern CONCURRENT_TYPE_NAME_PATTERN = Pattern.compile("(java\\.util\\.concurrent\\.[A-Za-z]+)<([^>]+)", Pattern.DOTALL | Pattern.UNIX_LINES); - - public enum StatementParameterType - { - STRING, LITERAL, TYPE, RAW - } - - @Inject - @Named("application.path") - protected String applicationPath; - - protected String packageName; - protected String className; - protected String sourceString; - - @Inject - @Named("registeredEndpoints") - protected Set registeredEndpoints; - - protected Class controllerClass; - - protected Set injectedHandlerWrappers = new HashSet<>(); - - @Inject - @Named("registeredHandlerWrappers") - protected Map registeredHandlerWrappers; - - protected Map, String> typeLevelHandlerWrapperMap = new LinkedHashMap, String>(); - - protected Map, String> handlerWrapperMap = new LinkedHashMap, String>(); - - /** - * Create a new {@code HandlerGenerator} instance used to generate a - * {@code Supplier} class - * @param packageName - * generated class package name - * @param controllerClass - * the class handlers will be generated from this class - */ - public HandlerGenerator(String packageName, Class controllerClass) - { - this.packageName = packageName; - this.controllerClass = controllerClass; - this.className = controllerClass.getSimpleName() + "RouteSupplier"; - } - - /** - * Compiles the generated source into a new {@link Class} - * @return a new {@code Supplier} class - */ - public Class> compileClass() - { - try - { - this.generateRoutes(); - - log.debug("\n\nGenerated Class Source:\n\n" + this.sourceString); - - return CompilerUtils.CACHED_COMPILER.loadFromJava(packageName + "." + className, this.sourceString); - - } catch (Exception e) - { - log.error(e.getMessage(), e); - return null; - } - } - - /** - * Generates the routing Java source code - */ - protected void generateRoutes() - { - try - { - +public class HandlerGenerator { + + static Logger log = LoggerFactory.getLogger(HandlerGenerator.class.getCanonicalName()); + + private static final Pattern TYPE_NAME_PATTERN = Pattern.compile("(java\\.util\\.[A-Za-z]+)<([^>]+)", Pattern.DOTALL | Pattern.UNIX_LINES); + private static final Pattern CONCURRENT_TYPE_NAME_PATTERN = Pattern.compile("(java\\.util\\.concurrent\\.[A-Za-z]+)<([^>]+)", Pattern.DOTALL | Pattern.UNIX_LINES); + + public enum StatementParameterType { + STRING, LITERAL, TYPE, RAW + } + + @Inject + @Named("application.path") + protected String applicationPath; + + protected String packageName; + protected String className; + protected String sourceString; + + @Inject + @Named("registeredEndpoints") + protected Set registeredEndpoints; + + protected Class controllerClass; + + protected Set injectedHandlerWrappers = new HashSet<>(); + + @Inject + @Named("registeredHandlerWrappers") + protected Map registeredHandlerWrappers; + + protected Map, String> typeLevelHandlerWrapperMap = new LinkedHashMap, String>(); + + protected Map, String> handlerWrapperMap = new LinkedHashMap, String>(); + + /** + * Create a new {@code HandlerGenerator} instance used to generate a + * {@code Supplier} class + * @param packageName + * generated class package name + * @param controllerClass + * the class handlers will be generated from this class + */ + public HandlerGenerator(String packageName, Class controllerClass) { + this.packageName = packageName; + this.controllerClass = controllerClass; + this.className = controllerClass.getSimpleName() + "RouteSupplier"; + } + + /** + * Compiles the generated source into a new {@link Class} + * @return a new {@code Supplier} class + */ + public Class> compileClass() { + try { + this.generateRoutes(); + + log.debug("\n\nGenerated Class Source:\n\n" + this.sourceString); + + return CompilerUtils.CACHED_COMPILER.loadFromJava(packageName + "." + className, this.sourceString); + + } catch (Exception e) { + log.error(e.getMessage(), e); + return null; + } + } + + /** + * Generates the routing Java source code + */ + protected void generateRoutes() { + try { + // Optional typeLevelWrapAnnotation = Optional.ofNullable(controllerClass.getAnnotation(io.sinistral.proteus.annotations.Chain.class)); // // typeLevelWrapAnnotation.ifPresent( a -> { @@ -200,327 +192,287 @@ protected void generateRoutes() // // log.info("handlerWrapperMap: " + handlerWrapperMap); - TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC) - .addSuperinterface(ParameterizedTypeName.get(Supplier.class, RoutingHandler.class)); + TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC) + .addSuperinterface(ParameterizedTypeName.get(Supplier.class, RoutingHandler.class)); + + ClassName extractorClass = ClassName.get("io.sinistral.proteus.server", "Extractors"); + + ClassName injectClass = ClassName.get("com.google.inject", "Inject"); + + + MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addAnnotation(injectClass); + + String className = this.controllerClass.getSimpleName().toLowerCase() + "Controller"; + + typeBuilder.addField(this.controllerClass, className, Modifier.PROTECTED, Modifier.FINAL); + + ClassName wrapperClass = ClassName.get("io.undertow.server", "HandlerWrapper"); + ClassName stringClass = ClassName.get("java.lang", "String"); + ClassName mapClass = ClassName.get("java.util", "Map"); + + TypeName mapOfWrappers = ParameterizedTypeName.get(mapClass, stringClass, wrapperClass); + + TypeName annotatedMapOfWrappers = mapOfWrappers + .annotated(AnnotationSpec.builder(com.google.inject.name.Named.class).addMember("value", "$S", "registeredHandlerWrappers").build()); + + typeBuilder.addField(mapOfWrappers, "registeredHandlerWrappers", Modifier.PROTECTED, Modifier.FINAL); + + + constructor.addParameter(this.controllerClass, className); + constructor.addParameter(annotatedMapOfWrappers, "registeredHandlerWrappers"); + + constructor.addStatement("this.$N = $N", className, className); + constructor.addStatement("this.$N = $N", "registeredHandlerWrappers", "registeredHandlerWrappers"); + + addClassMethodHandlers(typeBuilder, this.controllerClass); + + typeBuilder.addMethod(constructor.build()); + + JavaFile javaFile = JavaFile.builder(packageName, typeBuilder.build()).addStaticImport(extractorClass, "*").build(); + + StringBuilder sb = new StringBuilder(); + + javaFile.writeTo(sb); + + this.sourceString = sb.toString(); + + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class clazz) throws Exception { + ClassName httpHandlerClass = ClassName.get("io.undertow.server", "HttpHandler"); + + String controllerName = clazz.getSimpleName().toLowerCase() + "Controller"; + + int handlerWrapperIndex = 1; + + HashSet handlerNameSet = new HashSet<>(); + + MethodSpec.Builder initBuilder = MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC).returns(RoutingHandler.class) + .addStatement("final $T router = new $T()", io.undertow.server.RoutingHandler.class, io.undertow.server.RoutingHandler.class); + + final Map parameterizedLiteralsNameMap = Arrays.stream(clazz.getDeclaredMethods()) + .filter(m -> m.getAnnotation(Path.class) != null) + .flatMap( + m -> Arrays.stream(m.getParameters()).map(Parameter::getParameterizedType) + .filter(t -> t.getTypeName().contains("<") && !t.getTypeName().contains("concurrent"))) + .distinct().filter(t -> + { + + TypeHandler handler = TypeHandler.forType(t); + return (handler.equals(TypeHandler.ModelType) || handler.equals(TypeHandler.OptionalModelType)); + + }).collect(Collectors.toMap(java.util.function.Function.identity(), HandlerGenerator::typeReferenceNameForParameterizedType)); + + + Arrays.stream(clazz.getDeclaredMethods()) + .filter(m -> m.getAnnotation(Path.class) != null) + .flatMap(m -> Arrays.stream(m.getParameters())) + .forEach(p -> + { + + BeanParam beanParam = p.getAnnotation(BeanParam.class); + + boolean isBeanParameter = beanParam != null; + + if (isBeanParameter) { + TypeHandler handler = TypeHandler.forType(p.getParameterizedType(), true); + + if (handler.equals(TypeHandler.BeanListValueOfType) + || handler.equals(TypeHandler.BeanListFromStringType) + || handler.equals(TypeHandler.OptionalBeanListValueOfType) + || handler.equals(TypeHandler.OptionalBeanListFromStringType)) { + parameterizedLiteralsNameMap.put(p.getParameterizedType(), HandlerGenerator.typeReferenceNameForParameterizedType(p.getParameterizedType())); + } + } - ClassName extractorClass = ClassName.get("io.sinistral.proteus.server", "Extractors"); + }); - ClassName injectClass = ClassName.get("com.google.inject", "Inject"); - + final Map literalsNameMap = Arrays.stream(clazz.getDeclaredMethods()) + .filter(m -> m.getAnnotation(Path.class) != null) + .flatMap(m -> Arrays.stream(m.getParameters()) + .map(Parameter::getParameterizedType)).filter(t -> + { + if (t.getTypeName().contains("java.util")) { + return false; + } - MethodSpec.Builder constructor = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addAnnotation(injectClass); + try { + Class optionalType = (Class) extractErasedType(t); - String className = this.controllerClass.getSimpleName().toLowerCase() + "Controller"; + if (optionalType != null) { + t = optionalType; + } - typeBuilder.addField(this.controllerClass, className, Modifier.PROTECTED, Modifier.FINAL); + } catch (Exception e) { - ClassName wrapperClass = ClassName.get("io.undertow.server", "HandlerWrapper"); - ClassName stringClass = ClassName.get("java.lang", "String"); - ClassName mapClass = ClassName.get("java.util", "Map"); + } - TypeName mapOfWrappers = ParameterizedTypeName.get(mapClass, stringClass, wrapperClass); + if (t.getTypeName().contains("java.lang")) { + return false; + } else if (t.getTypeName().contains("java.nio")) { + return false; + } else if (t.getTypeName().contains("java.io")) { + return false; + } else if (t.getTypeName().contains("java.util")) { + return false; + } else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) { + return false; + } - TypeName annotatedMapOfWrappers = mapOfWrappers - .annotated(AnnotationSpec.builder(com.google.inject.name.Named.class).addMember("value", "$S", "registeredHandlerWrappers").build()); + if (t instanceof Class) { + Class pClazz = (Class) t; + if (pClazz.isPrimitive()) { + return false; + } + if (pClazz.isEnum()) { + return false; + } - typeBuilder.addField(mapOfWrappers, "registeredHandlerWrappers", Modifier.PROTECTED, Modifier.FINAL); - + } - constructor.addParameter(this.controllerClass, className); - constructor.addParameter(annotatedMapOfWrappers, "registeredHandlerWrappers"); - - constructor.addStatement("this.$N = $N", className, className); - constructor.addStatement("this.$N = $N", "registeredHandlerWrappers", "registeredHandlerWrappers"); + return true; - addClassMethodHandlers(typeBuilder, this.controllerClass); - - typeBuilder.addMethod(constructor.build()); - - JavaFile javaFile = JavaFile.builder(packageName, typeBuilder.build()).addStaticImport(extractorClass, "*").build(); - - StringBuilder sb = new StringBuilder(); - - javaFile.writeTo(sb); - - this.sourceString = sb.toString(); - - } catch (Exception e) - { - log.error(e.getMessage(), e); - } - } - - protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class clazz) throws Exception - { - ClassName httpHandlerClass = ClassName.get("io.undertow.server", "HttpHandler"); - - String controllerName = clazz.getSimpleName().toLowerCase() + "Controller"; - - int handlerWrapperIndex = 1; - - HashSet handlerNameSet = new HashSet<>(); - - MethodSpec.Builder initBuilder = MethodSpec.methodBuilder("get").addModifiers(Modifier.PUBLIC).returns(RoutingHandler.class) - .addStatement("final $T router = new $T()", io.undertow.server.RoutingHandler.class, io.undertow.server.RoutingHandler.class); - - final Map parameterizedLiteralsNameMap = Arrays.stream(clazz.getDeclaredMethods()) - .filter(m -> m.getAnnotation(Path.class) != null) - .flatMap( - m -> Arrays.stream(m.getParameters()).map(Parameter::getParameterizedType) - .filter(t -> t.getTypeName().contains("<") && !t.getTypeName().contains("concurrent"))) - .distinct().filter(t -> - { - - TypeHandler handler = TypeHandler.forType(t); - return (handler.equals(TypeHandler.ModelType) || handler.equals(TypeHandler.OptionalModelType)); - - }).collect(Collectors.toMap(java.util.function.Function.identity(), HandlerGenerator::typeReferenceNameForParameterizedType)); - - - - Arrays.stream(clazz.getDeclaredMethods()) - .filter(m -> m.getAnnotation(Path.class) != null) - .flatMap(m -> Arrays.stream(m.getParameters())) - .forEach(p -> - { - - BeanParam beanParam = p.getAnnotation(BeanParam.class); - - boolean isBeanParameter = beanParam != null; - - if (isBeanParameter) - { - TypeHandler handler = TypeHandler.forType(p.getParameterizedType(), true); - - if (handler.equals(TypeHandler.BeanListValueOfType) - || handler.equals(TypeHandler.BeanListFromStringType) - || handler.equals(TypeHandler.OptionalBeanListValueOfType) - || handler.equals(TypeHandler.OptionalBeanListFromStringType)) - { - parameterizedLiteralsNameMap.put(p.getParameterizedType(), HandlerGenerator.typeReferenceNameForParameterizedType(p.getParameterizedType())); - } - } - - }); - - final Map literalsNameMap = Arrays.stream(clazz.getDeclaredMethods()) - .filter(m -> m.getAnnotation(Path.class) != null) - .flatMap(m -> Arrays.stream(m.getParameters()) - .map(Parameter::getParameterizedType)).filter(t -> - { - - if (t.getTypeName().contains("java.util")) - { - return false; - } - - try - { - Class optionalType = (Class) extractErasedType(t); - - if (optionalType != null) - { - t = optionalType; - } - - } catch (Exception e) - { - - } - - if (t.getTypeName().contains("java.lang")) - { - return false; - } - else if (t.getTypeName().contains("java.nio")) - { - return false; - } - else if (t.getTypeName().contains("java.io")) - { - return false; - } - else if (t.getTypeName().contains("java.util")) - { - return false; - } - else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) - { - return false; - } - - if (t instanceof Class) - { - Class pClazz = (Class) t; - if (pClazz.isPrimitive()) - { - return false; - } - if (pClazz.isEnum()) - { - return false; - } - - } - - return true; - - }) - .distinct() - .collect(Collectors.toMap(java.util.function.Function.identity(), HandlerGenerator::typeReferenceNameForType)); - - parameterizedLiteralsNameMap - .forEach((t, n) -> initBuilder.addStatement("final $T<$L> $LTypeReference = new $T<$L>(){}", TypeReference.class, t, n, TypeReference.class, t)); - - literalsNameMap.forEach((t, n) -> initBuilder.addStatement("final $T<$T> $LTypeReference = new $T<$T>(){}", TypeReference.class, t, n, TypeReference.class, t)); - - Optional typeLevelWrapAnnotation = Optional.ofNullable(clazz.getAnnotation(io.sinistral.proteus.annotations.Chain.class)); - - - if (typeLevelWrapAnnotation.isPresent()) - { - io.sinistral.proteus.annotations.Chain w = typeLevelWrapAnnotation.get(); - - Class wrapperClasses[] = w.value(); - - for (int i = 0; i < wrapperClasses.length; i++) - { - Class wrapperClass = wrapperClasses[i]; - - String wrapperName = generateFieldName(wrapperClass.getCanonicalName()); - - initBuilder.addStatement("final $T $L = new $T()", wrapperClass, wrapperName, wrapperClass); - - typeLevelHandlerWrapperMap.put(wrapperClass, wrapperName); - } - } - - initBuilder.addStatement("$T currentHandler = $L", HttpHandler.class, "null"); - - initBuilder.addCode("$L", "\n"); - - List consumesContentTypes = new ArrayList<>(); - List producesContentTypes = new ArrayList<>(); + }) + .distinct() + .collect(Collectors.toMap(java.util.function.Function.identity(), HandlerGenerator::typeReferenceNameForType)); - /* - * Controller Level Authorization - */ + parameterizedLiteralsNameMap + .forEach((t, n) -> initBuilder.addStatement("final $T<$L> $LTypeReference = new $T<$L>(){}", TypeReference.class, t, n, TypeReference.class, t)); - List typeLevelSecurityDefinitions = new ArrayList<>(); + literalsNameMap.forEach((t, n) -> initBuilder.addStatement("final $T<$T> $LTypeReference = new $T<$T>(){}", TypeReference.class, t, n, TypeReference.class, t)); - if (Optional.ofNullable(clazz.getAnnotation(Path.class)).isPresent()) - { - SecurityRequirement securityRequirementAnnotation = clazz.getAnnotation(SecurityRequirement.class); + Optional typeLevelWrapAnnotation = Optional.ofNullable(clazz.getAnnotation(io.sinistral.proteus.annotations.Chain.class)); - if(securityRequirementAnnotation != null) - { - String securityRequirement = securityRequirementAnnotation.name(); - - typeLevelSecurityDefinitions.add(securityRequirement); - } - } - log.debug("Scanning methods for class " + clazz.getName()); + if (typeLevelWrapAnnotation.isPresent()) { + io.sinistral.proteus.annotations.Chain w = typeLevelWrapAnnotation.get(); - int nameIndex = 1; + Class wrapperClasses[] = w.value(); - for (Method m : clazz.getDeclaredMethods()) - { + for (int i = 0; i < wrapperClasses.length; i++) { + Class wrapperClass = wrapperClasses[i]; - if (!Optional.ofNullable(m.getAnnotation(javax.ws.rs.Path.class)).isPresent()) - { - continue; - } + String wrapperName = generateFieldName(wrapperClass.getCanonicalName()); - log.debug("Scanning method " + m.getName() + "\n"); + initBuilder.addStatement("final $T $L = new $T()", wrapperClass, wrapperName, wrapperClass); - EndpointInfo endpointInfo = new EndpointInfo(); + typeLevelHandlerWrapperMap.put(wrapperClass, wrapperName); + } + } + + initBuilder.addStatement("$T currentHandler = $L", HttpHandler.class, "null"); + + initBuilder.addCode("$L", "\n"); + + List consumesContentTypes = new ArrayList<>(); + List producesContentTypes = new ArrayList<>(); + + /* + * Controller Level Authorization + */ + + List typeLevelSecurityDefinitions = new ArrayList<>(); + + if (Optional.ofNullable(clazz.getAnnotation(Path.class)).isPresent()) { + SecurityRequirement securityRequirementAnnotation = clazz.getAnnotation(SecurityRequirement.class); + + if (securityRequirementAnnotation != null) { + String securityRequirement = securityRequirementAnnotation.name(); + + typeLevelSecurityDefinitions.add(securityRequirement); + } + } + + log.debug("Scanning methods for class " + clazz.getName()); + + int nameIndex = 1; + + for (Method m : clazz.getDeclaredMethods()) { - String producesContentType = "*/*"; - String consumesContentType = "*/*"; + if (!Optional.ofNullable(m.getAnnotation(javax.ws.rs.Path.class)).isPresent()) { + continue; + } - Boolean isBlocking = false; - Boolean isDebug = false; + log.debug("Scanning method " + m.getName() + "\n"); - Optional blockingAnnotation = Optional.ofNullable(m.getAnnotation(Blocking.class)); + EndpointInfo endpointInfo = new EndpointInfo(); - if (blockingAnnotation.isPresent()) - { - isBlocking = blockingAnnotation.get().value(); - } - - Optional debugAnnotation = Optional.ofNullable(m.getAnnotation(Debug.class)); + String producesContentType = "*/*"; + String consumesContentType = "*/*"; - if (debugAnnotation.isPresent()) - { - isDebug = debugAnnotation.get().value(); - } + Boolean isBlocking = false; + Boolean isDebug = false; - Optional producesAnnotation = Optional.ofNullable(m.getAnnotation(javax.ws.rs.Produces.class)); + Optional blockingAnnotation = Optional.ofNullable(m.getAnnotation(Blocking.class)); - if (!producesAnnotation.isPresent()) - { - producesAnnotation = Optional.ofNullable(clazz.getAnnotation(javax.ws.rs.Produces.class)); + if (blockingAnnotation.isPresent()) { + isBlocking = blockingAnnotation.get().value(); + } - if (producesAnnotation.isPresent()) - { + Optional debugAnnotation = Optional.ofNullable(m.getAnnotation(Debug.class)); - producesContentTypes = Arrays.stream(producesAnnotation.get().value()).flatMap(v -> Arrays.stream((v.split(",")))).collect(Collectors.toList()); + if (debugAnnotation.isPresent()) { + isDebug = debugAnnotation.get().value(); + } - producesContentType = producesContentTypes.stream().collect(Collectors.joining(",")); - } + Optional producesAnnotation = Optional.ofNullable(m.getAnnotation(javax.ws.rs.Produces.class)); - } - else - { - producesContentTypes = Arrays.stream(producesAnnotation.get().value()).flatMap(v -> Arrays.stream((v.split(",")))).collect(Collectors.toList()); + if (!producesAnnotation.isPresent()) { + producesAnnotation = Optional.ofNullable(clazz.getAnnotation(javax.ws.rs.Produces.class)); - producesContentType = producesContentTypes.stream().collect(Collectors.joining(",")); - } + if (producesAnnotation.isPresent()) { - endpointInfo.setProduces(producesContentType); + producesContentTypes = Arrays.stream(producesAnnotation.get().value()).flatMap(v -> Arrays.stream((v.split(",")))).collect(Collectors.toList()); - Optional consumesAnnotation = Optional.ofNullable(m.getAnnotation(javax.ws.rs.Consumes.class)); + producesContentType = producesContentTypes.stream().collect(Collectors.joining(",")); + } - if (!consumesAnnotation.isPresent()) - { - consumesAnnotation = Optional.ofNullable(clazz.getAnnotation(javax.ws.rs.Consumes.class)); + } else { + producesContentTypes = Arrays.stream(producesAnnotation.get().value()).flatMap(v -> Arrays.stream((v.split(",")))).collect(Collectors.toList()); - if (consumesAnnotation.isPresent()) - { - consumesContentTypes = Arrays.stream(consumesAnnotation.get().value()).flatMap(v -> Arrays.stream((v.split(",")))).collect(Collectors.toList()); + producesContentType = producesContentTypes.stream().collect(Collectors.joining(",")); + } - consumesContentType = consumesContentTypes.stream().collect(Collectors.joining(",")); - } - } - else - { - consumesContentTypes = Arrays.stream(consumesAnnotation.get().value()).flatMap(v -> Arrays.stream((v.split(",")))).collect(Collectors.toList()); + endpointInfo.setProduces(producesContentType); - consumesContentType = consumesContentTypes.stream().collect(Collectors.joining(",")); - } + Optional consumesAnnotation = Optional.ofNullable(m.getAnnotation(javax.ws.rs.Consumes.class)); - endpointInfo.setControllerName(clazz.getSimpleName()); + if (!consumesAnnotation.isPresent()) { + consumesAnnotation = Optional.ofNullable(clazz.getAnnotation(javax.ws.rs.Consumes.class)); - String methodPath = null; + if (consumesAnnotation.isPresent()) { + consumesContentTypes = Arrays.stream(consumesAnnotation.get().value()).flatMap(v -> Arrays.stream((v.split(",")))).collect(Collectors.toList()); - try - { - methodPath = Extractors.pathTemplateFromMethod.apply(m).replaceAll("\\/\\/", "\\/"); - } catch (Exception e) - { - log.error(e.getMessage() + " for " + m, e); - continue; - } + consumesContentType = consumesContentTypes.stream().collect(Collectors.joining(",")); + } + } else { + consumesContentTypes = Arrays.stream(consumesAnnotation.get().value()).flatMap(v -> Arrays.stream((v.split(",")))).collect(Collectors.toList()); - methodPath = applicationPath + methodPath; + consumesContentType = consumesContentTypes.stream().collect(Collectors.joining(",")); + } + + endpointInfo.setControllerName(clazz.getSimpleName()); - HttpString httpMethod = Extractors.httpMethodFromMethod.apply(m); + String methodPath = null; - endpointInfo.setMethod(httpMethod); + try { + methodPath = Extractors.pathTemplateFromMethod.apply(m).replaceAll("\\/\\/", "\\/"); + } catch (Exception e) { + log.error(e.getMessage() + " for " + m, e); + continue; + } - endpointInfo.setConsumes(consumesContentType); + methodPath = applicationPath + methodPath; + + HttpString httpMethod = Extractors.httpMethodFromMethod.apply(m); + + endpointInfo.setMethod(httpMethod); + + endpointInfo.setConsumes(consumesContentType); //The handler for these two inputs types is blocking, so we set the flag if (endpointInfo.getConsumes().contains(FormEncodedDataDefinition.APPLICATION_X_WWW_FORM_URLENCODED) @@ -528,741 +480,625 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) isBlocking = true; } - endpointInfo.setPathTemplate(methodPath); - - endpointInfo.setControllerMethod(m.getName()); - - String handlerName = String.format("%c%s%sHandler_%s", Character.toLowerCase(clazz.getSimpleName().charAt(0)), clazz.getSimpleName() - .substring(1), StringUtils.capitalize(m.getName()), String.valueOf(nameIndex++)); - - handlerNameSet.add(handlerName); - - TypeSpec.Builder handlerClassBuilder = TypeSpec.anonymousClassBuilder("").addSuperinterface(httpHandlerClass); - - MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("handleRequest").addModifiers(Modifier.PUBLIC).addException(ClassName.get("java.lang", "Exception")) - .addAnnotation(Override.class) - .addParameter(ParameterSpec.builder(HttpServerExchange.class, "exchange", Modifier.FINAL).build()); - - for (Parameter p : m.getParameters()) - { - - if (p.getParameterizedType().equals(ServerRequest.class) - || p.getParameterizedType().equals(HttpServerExchange.class) - || p.getParameterizedType().equals(HttpHandler.class)) - { - continue; - } - - try - { - BeanParam beanParam = p.getAnnotation(BeanParam.class); - - boolean isBeanParameter = beanParam != null; - - TypeHandler t = TypeHandler.forType(p.getParameterizedType(), isBeanParameter); - - if (t.isBlocking()) - { - isBlocking = true; - break; - } - - } catch (Exception e) - { - log.error(e.getMessage(), e); - } - } - - log.debug("parameterizedLiteralsNameMap: " + parameterizedLiteralsNameMap); - - if(isBlocking) - { - - methodBuilder.addCode( - "$L.dispatch( () -> ", - "exchange"); - - methodBuilder.beginControlFlow("", ""); - - methodBuilder.beginControlFlow("try"); - - } - - Arrays.stream(m.getParameters()).forEachOrdered(p -> - { - - Type type = p.getParameterizedType(); - - try - { - - log.debug("Parameter " + p.getName() + " of type " + type); - - if (p.getType().equals(ServerRequest.class)) - { - methodBuilder.addStatement("$T $L = new $T(exchange)", ServerRequest.class, p.getName(), ServerRequest.class); - - } - else if (p.getType().equals(HttpHandler.class)) - { - methodBuilder.addStatement("$T $L = this", HttpHandler.class, p.getName()); - } - else if(!p.getType().equals(HttpServerExchange.class)) - { - if (p.isAnnotationPresent(HeaderParam.class)) - { - - TypeHandler handler = TypeHandler.forType(type); - - if (handler.equals(TypeHandler.OptionalStringType)) - { - handler = TypeHandler.OptionalHeaderStringType; - - TypeHandler.addStatement(methodBuilder, p, handler); - - } - else if (handler.equals(TypeHandler.OptionalValueOfType)) - { - handler = TypeHandler.OptionalHeaderValueOfType; - - TypeHandler.addStatement(methodBuilder, p, handler); - - } - else if (handler.equals(TypeHandler.OptionalFromStringType)) - { - handler = TypeHandler.OptionalHeaderFromStringType; - TypeHandler.addStatement(methodBuilder, p, handler); - - } - else if (handler.equals(TypeHandler.StringType)) - { - handler = TypeHandler.HeaderStringType; - TypeHandler.addStatement(methodBuilder, p, handler); - - } - else if (handler.equals(TypeHandler.ValueOfType)) - { - handler = TypeHandler.HeaderValueOfType; - TypeHandler.addStatement(methodBuilder, p, handler); - - } - else if (handler.equals(TypeHandler.FromStringType)) - { - handler = TypeHandler.HeaderFromStringType; - TypeHandler.addStatement(methodBuilder, p, handler); - } - else - { - handler = TypeHandler.HeaderStringType; - - TypeHandler.addStatement(methodBuilder, p, handler); - } - - } - else - { - BeanParam beanParam = p.getAnnotation(BeanParam.class); - - boolean isBeanParameter = beanParam != null; - - TypeHandler t = TypeHandler.forType(type, isBeanParameter); - - log.debug("beanParam handler: " + t); - - if (t.equals(TypeHandler.OptionalModelType) || t.equals(TypeHandler.ModelType)) - { - String interfaceType = parameterizedLiteralsNameMap.get(type); - - String typeName = type.getTypeName(); - - if (typeName.contains("$")) - { - typeName = typeName.replace("$", "."); - } - - String pType = interfaceType != null ? interfaceType + "TypeReference" : typeName + ".class"; - - methodBuilder.addStatement(t.statement, type, p.getName(), pType); - - } - else if (t.equals(TypeHandler.BeanListFromStringType) || t.equals(TypeHandler.BeanListValueOfType)) - { - String interfaceType = parameterizedLiteralsNameMap.get(type); + endpointInfo.setPathTemplate(methodPath); + + endpointInfo.setControllerMethod(m.getName()); + + String handlerName = String.format("%c%s%sHandler_%s", Character.toLowerCase(clazz.getSimpleName().charAt(0)), clazz.getSimpleName() + .substring(1), StringUtils.capitalize(m.getName()), String.valueOf(nameIndex++)); + + handlerNameSet.add(handlerName); + + TypeSpec.Builder handlerClassBuilder = TypeSpec.anonymousClassBuilder("").addSuperinterface(httpHandlerClass); + + /** + * @TODO + * Rewrite with lambdas or method references. + * + * final io.undertow.server.HttpHandler benchmarksDbPostgresHandler = (final HttpServerExchange exchange) -> + * { + * benchmarksController.dbPostgres; + * }); + * + * OR + * + * final io.undertow.server.HttpHandler benchmarksDbPostgresHandler = benchmarksController::dbPostgres; + **/ + + MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("handleRequest").addModifiers(Modifier.PUBLIC).addException(ClassName.get("java.lang", "Exception")) + .addAnnotation(Override.class) + .addParameter(ParameterSpec.builder(HttpServerExchange.class, "exchange", Modifier.FINAL).build()); + + for (Parameter p : m.getParameters()) { + + if (p.getParameterizedType().equals(ServerRequest.class) + || p.getParameterizedType().equals(HttpServerExchange.class) + || p.getParameterizedType().equals(HttpHandler.class)) { + continue; + } + + try { + BeanParam beanParam = p.getAnnotation(BeanParam.class); + + boolean isBeanParameter = beanParam != null; + + TypeHandler t = TypeHandler.forType(p.getParameterizedType(), isBeanParameter); + + if (t.isBlocking()) { + isBlocking = true; + break; + } + + } catch (Exception e) { + log.error(e.getMessage(), e); + } + } + + log.debug("parameterizedLiteralsNameMap: " + parameterizedLiteralsNameMap); + + if (isBlocking) { + + methodBuilder.addCode( + "$L.dispatch( () -> ", + "exchange"); + + methodBuilder.beginControlFlow("", ""); + + methodBuilder.beginControlFlow("try"); + + } + + Arrays.stream(m.getParameters()).forEachOrdered(p -> + { + + Type type = p.getParameterizedType(); + + try { + + log.debug("Parameter " + p.getName() + " of type " + type); + + if (p.getType().equals(ServerRequest.class)) { + methodBuilder.addStatement("$T $L = new $T(exchange)", ServerRequest.class, p.getName(), ServerRequest.class); + + } else if (p.getType().equals(HttpHandler.class)) { + methodBuilder.addStatement("$T $L = this", HttpHandler.class, p.getName()); + } else if (!p.getType().equals(HttpServerExchange.class)) { + if (p.isAnnotationPresent(HeaderParam.class)) { + + TypeHandler handler = TypeHandler.forType(type); + + if (handler.equals(TypeHandler.OptionalStringType)) { + handler = TypeHandler.OptionalHeaderStringType; + + TypeHandler.addStatement(methodBuilder, p, handler); + + } else if (handler.equals(TypeHandler.OptionalValueOfType)) { + handler = TypeHandler.OptionalHeaderValueOfType; + + TypeHandler.addStatement(methodBuilder, p, handler); + + } else if (handler.equals(TypeHandler.OptionalFromStringType)) { + handler = TypeHandler.OptionalHeaderFromStringType; + TypeHandler.addStatement(methodBuilder, p, handler); + + } else if (handler.equals(TypeHandler.StringType)) { + handler = TypeHandler.HeaderStringType; + TypeHandler.addStatement(methodBuilder, p, handler); + + } else if (handler.equals(TypeHandler.ValueOfType)) { + handler = TypeHandler.HeaderValueOfType; + TypeHandler.addStatement(methodBuilder, p, handler); + + } else if (handler.equals(TypeHandler.FromStringType)) { + handler = TypeHandler.HeaderFromStringType; + TypeHandler.addStatement(methodBuilder, p, handler); + } else { + handler = TypeHandler.HeaderStringType; + + TypeHandler.addStatement(methodBuilder, p, handler); + } + + } else { + BeanParam beanParam = p.getAnnotation(BeanParam.class); + + boolean isBeanParameter = beanParam != null; + + TypeHandler t = TypeHandler.forType(type, isBeanParameter); + + log.debug("beanParam handler: " + t); + + if (t.equals(TypeHandler.OptionalModelType) || t.equals(TypeHandler.ModelType)) { + String interfaceType = parameterizedLiteralsNameMap.get(type); + + String typeName = type.getTypeName(); + + if (typeName.contains("$")) { + typeName = typeName.replace("$", "."); + } + + String pType = interfaceType != null ? interfaceType + "TypeReference" : typeName + ".class"; + + methodBuilder.addStatement(t.statement, type, p.getName(), pType); + + } else if (t.equals(TypeHandler.BeanListFromStringType) || t.equals(TypeHandler.BeanListValueOfType)) { + String interfaceType = parameterizedLiteralsNameMap.get(type); + + String typeName = type.getTypeName(); + + if (typeName.contains("$")) { + typeName = typeName.replace("$", "."); + } + + String pType = interfaceType != null ? interfaceType + "TypeReference" : typeName + ".class"; + + methodBuilder.addStatement(t.statement, type, p.getName(), pType); + + } else if (t.equals(TypeHandler.OptionalFromStringType) || t.equals(TypeHandler.OptionalValueOfType)) { + + TypeHandler.addStatement(methodBuilder, p); + } else if (t.equals(TypeHandler.QueryOptionalListFromStringType) + || t.equals(TypeHandler.QueryOptionalListValueOfType) + || t.equals(TypeHandler.QueryOptionalSetValueOfType) + || t.equals(TypeHandler.QueryOptionalSetFromStringType)) { + ParameterizedType pType = (ParameterizedType) type; + + if (type instanceof ParameterizedType) { + pType = (ParameterizedType) type; + type = pType.getActualTypeArguments()[0]; + } + + Class erasedType = (Class) extractErasedType(type); + + methodBuilder.addStatement(t.statement, pType, p.getName(), p.getName(), erasedType); + + } else if (t.equals(TypeHandler.OptionalBeanListFromStringType) || t.equals(TypeHandler.OptionalBeanListValueOfType)) { + ParameterizedType pType = (ParameterizedType) type; + + if (type instanceof ParameterizedType) { + pType = (ParameterizedType) type; + type = pType.getActualTypeArguments()[0]; + } + + Class erasedType = (Class) extractErasedType(type); + + try { + + methodBuilder.addStatement(t.statement, pType, p.getName(), p.getName(), erasedType); + + } catch (Exception e) { + log.error("method builder: \nstatement: " + t.statement + "\npType: " + pType + "\np.name(): " + p.getName() + "\nerasedType: " + erasedType); + } + + } else { + TypeHandler.addStatement(methodBuilder, p); + } + } + } + + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + }); + + methodBuilder.addCode("$L", "\n"); + + CodeBlock.Builder functionBlockBuilder = CodeBlock.builder(); + + String controllerMethodArgs = Arrays.stream(m.getParameters()).map(Parameter::getName).collect(Collectors.joining(",")); + + if (!m.getReturnType().toString().equalsIgnoreCase("void")) { + if (m.getReturnType().getTypeName().contains("java.util.concurrent.CompletionStage") + || m.getReturnType().getTypeName().contains("java.util.concurrent.CompletableFuture")) { + Type futureType = m.getGenericReturnType(); + + functionBlockBuilder.add("$T $L = $L.$L($L);", futureType, "response", controllerName, m.getName(), controllerMethodArgs); + + } else { + functionBlockBuilder.add("$T $L = $L.$L($L);", m.getGenericReturnType(), "response", controllerName, m.getName(), controllerMethodArgs); + } + + methodBuilder.addCode(functionBlockBuilder.build()); + + methodBuilder.addCode("$L", "\n"); + + if (m.getReturnType().equals(ServerResponse.class)) { + methodBuilder.addStatement("$L.send(this,$L)", "response", "exchange"); + + } else if (m.getReturnType().getTypeName().contains("java.util.concurrent.CompletionStage") + || m.getReturnType().getTypeName().contains("java.util.concurrent.CompletableFuture")) { + String postProcess = "."; + + if (!producesContentType.contains(",")) { + if (producesContentType.contains(MediaType.APPLICATION_JSON)) { + postProcess = ".applicationJson()."; + } else if (producesContentType.contains(MediaType.APPLICATION_XML)) { + postProcess = ".applicationXml()."; + } else if (producesContentType.contains(MediaType.TEXT_HTML)) { + postProcess = ".textHtml()."; + } + } + + methodBuilder.addCode( + "$L.thenAcceptAsync( r -> r" + postProcess + "send(this,$L), io.undertow.util.SameThreadExecutor.INSTANCE )\n\t.exceptionally( ex -> ", + "response", "exchange"); + methodBuilder.beginControlFlow("", ""); + methodBuilder.addCode("\t\tthrow new java.util.concurrent.CompletionException(ex);\n\t"); + methodBuilder.endControlFlow(")", ""); + } else { + + methodBuilder.addStatement("exchange.getResponseHeaders().put($T.CONTENT_TYPE, $S)", Headers.class, producesContentType); + + if (m.getReturnType().equals(String.class)) { + methodBuilder.addStatement("exchange.getResponseHeaders().send($L)", "response"); + } else { + methodBuilder.addStatement("exchange.getResponseSender().send($L.toString())", "response"); + } + + } + + + } else { + + functionBlockBuilder.add("$L.$L($L);", controllerName, m.getName(), controllerMethodArgs); + + methodBuilder.addCode(functionBlockBuilder.build()); + + methodBuilder.addCode("$L", "\n"); + + + } + + if (isBlocking) { + methodBuilder.endControlFlow("catch ($T e) { \n\texchange.putAttachment(io.sinistral.proteus.server.handlers.ServerDefaultResponseListener.EXCEPTION, e);\n\texchange.endExchange();\n}", Exception.class); + methodBuilder.endControlFlow(")", ""); + + } + + handlerClassBuilder.addMethod(methodBuilder.build()); + + + FieldSpec handlerField = FieldSpec.builder(httpHandlerClass, handlerName, Modifier.FINAL).initializer("$L", handlerClassBuilder.build()).build(); + + initBuilder.addCode("$L\n", handlerField.toString()); + + Optional wrapAnnotation = Optional.ofNullable(m.getAnnotation(io.sinistral.proteus.annotations.Chain.class)); + + /* + * Authorization + */ + + List securityDefinitions = new ArrayList<>(); + + /* + * @TODO wrap blocking in BlockingHandler + */ + + if (Optional.ofNullable(m.getAnnotation(Path.class)).isPresent()) { + SecurityRequirement securityRequirementAnnotation = m.getAnnotation(SecurityRequirement.class); + + if (securityRequirementAnnotation != null) { + String securityRequirement = securityRequirementAnnotation.name(); + + securityDefinitions.add(securityRequirement); + } + + } + + if (securityDefinitions.isEmpty()) { + securityDefinitions.addAll(typeLevelSecurityDefinitions); + } + + if (isBlocking && isDebug) { + handlerName = "new io.undertow.server.handlers.RequestDumpingHandler(new io.undertow.server.handlers.BlockingHandler(new io.undertow.server.handlers.RequestBufferingHandler.Wrapper(8).wrap(" + handlerName + ")))"; + } else if (isBlocking) { + handlerName = "new io.undertow.server.handlers.BlockingHandler(new io.undertow.server.handlers.RequestBufferingHandler.Wrapper(8).wrap(" + handlerName + "))"; - String typeName = type.getTypeName(); + } else if (isDebug) { + handlerName = "new io.undertow.server.handlers.RequestDumpingHandler(" + handlerName + ")"; - if (typeName.contains("$")) - { - typeName = typeName.replace("$", "."); - } + } + + if (wrapAnnotation.isPresent() || typeLevelHandlerWrapperMap.size() > 0 || securityDefinitions.size() > 0) { + initBuilder.addStatement("currentHandler = $L", handlerName); + + if (wrapAnnotation.isPresent()) { + io.sinistral.proteus.annotations.Chain w = wrapAnnotation.get(); + + Class[] wrapperClasses = w.value(); + + for (Class wrapperClass : wrapperClasses) { + String wrapperName = typeLevelHandlerWrapperMap.get(wrapperClass); + + if (wrapperName == null) { + wrapperName = String.format("%s_%d", generateFieldName(wrapperClass.getCanonicalName()), handlerWrapperIndex++); + + initBuilder.addStatement("final $T $L = new $T()", wrapperClass, wrapperName, wrapperClass); + } + + initBuilder.addStatement("currentHandler = $L.wrap($L)", wrapperName, "currentHandler"); + } + } + + for (Class wrapperClass : typeLevelHandlerWrapperMap.keySet()) { + String wrapperName = typeLevelHandlerWrapperMap.get(wrapperClass); + initBuilder.addStatement("currentHandler = $L.wrap($L)", wrapperName, "currentHandler"); + } + + for (String securityDefinitionName : securityDefinitions) { + initBuilder.addStatement("currentHandler = registeredHandlerWrappers.get($S).wrap($L)", securityDefinitionName, "currentHandler"); + } + + initBuilder.addStatement("$L.add(io.undertow.util.Methods.$L,$S,$L)", "router", httpMethod, methodPath, "currentHandler"); + } else { + initBuilder.addStatement("$L.add(io.undertow.util.Methods.$L,$S,$L)", "router", httpMethod, methodPath, handlerName); + } + + initBuilder.addCode("$L", "\n"); + + registeredEndpoints.add(endpointInfo); + + } + + initBuilder.addCode("$Lreturn router;\n", "\n"); + + typeBuilder.addMethod(initBuilder.build()); + + } + + /** + * @return the packageName + */ + public String getPackageName() { + return packageName; + } + + /** + * @param packageName + * the packageName to set + */ + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + /** + * @return the className + */ + public String getClassName() { + return className; + } + + /** + * @param className + * the className to set + */ + public void setClassName(String className) { + this.className = className; + } + + protected static ArrayList getClassNamesFromPackage(String packageName) throws Exception { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + URL packageURL; + ArrayList names = new ArrayList<>(); + + packageName = packageName.replace(".", "/"); + packageURL = classLoader.getResource(packageName); + + assert packageURL != null; + URI uri = new URI(packageURL.toString()); + File folder = new File(uri.getPath()); + // won't work with path which contains blank (%20) + // File folder = new File(packageURL.getFile()); + File[] contenuti = folder.listFiles(); + String entryName; + assert contenuti != null; + for (File actual : contenuti) { + if (actual.isDirectory()) { + continue; + } + + entryName = actual.getName(); + entryName = entryName.substring(0, entryName.lastIndexOf('.')); + names.add(entryName); + } - String pType = interfaceType != null ? interfaceType + "TypeReference" : typeName + ".class"; + return names; + } - methodBuilder.addStatement(t.statement, type, p.getName(), pType); - - } - else if (t.equals(TypeHandler.OptionalFromStringType) || t.equals(TypeHandler.OptionalValueOfType)) - { - - TypeHandler.addStatement(methodBuilder, p); - } - else if (t.equals(TypeHandler.QueryOptionalListFromStringType) - || t.equals(TypeHandler.QueryOptionalListValueOfType) - || t.equals(TypeHandler.QueryOptionalSetValueOfType) - || t.equals(TypeHandler.QueryOptionalSetFromStringType)) - { - ParameterizedType pType = (ParameterizedType) type; - - if (type instanceof ParameterizedType) - { - pType = (ParameterizedType) type; - type = pType.getActualTypeArguments()[0]; - } - - Class erasedType = (Class) extractErasedType(type); - - methodBuilder.addStatement(t.statement, pType, p.getName(), p.getName(), erasedType); - - } - else if (t.equals(TypeHandler.OptionalBeanListFromStringType) || t.equals(TypeHandler.OptionalBeanListValueOfType)) - { - ParameterizedType pType = (ParameterizedType) type; - - if (type instanceof ParameterizedType) - { - pType = (ParameterizedType) type; - type = pType.getActualTypeArguments()[0]; - } - - Class erasedType = (Class) extractErasedType(type); - - try - { - - methodBuilder.addStatement(t.statement, pType, p.getName(), p.getName(), erasedType); - - } catch (Exception e) - { - log.error("method builder: \nstatement: " + t.statement + "\npType: " + pType + "\np.name(): " + p.getName() + "\nerasedType: " + erasedType); - } - - } - else - { - TypeHandler.addStatement(methodBuilder, p); - } - } - } - - } catch (Exception e) - { - log.error(e.getMessage(), e); - } - - }); + protected static Set> getApiClasses(String basePath, Predicate pathPredicate) throws Exception { - methodBuilder.addCode("$L", "\n"); + Reflections ref = new Reflections(basePath); + Stream> stream = ref.getTypesAnnotatedWith(Path.class).stream(); - CodeBlock.Builder functionBlockBuilder = CodeBlock.builder(); + if (pathPredicate != null) { + stream = stream.filter(clazz -> + { - String controllerMethodArgs = Arrays.stream(m.getParameters()).map(Parameter::getName).collect(Collectors.joining(",")); + Path annotation = clazz.getDeclaredAnnotation(Path.class); - if (!m.getReturnType().toString().equalsIgnoreCase("void")) - { - if (m.getReturnType().getTypeName().contains("java.util.concurrent.CompletionStage") - || m.getReturnType().getTypeName().contains("java.util.concurrent.CompletableFuture")) - { - Type futureType = m.getGenericReturnType(); + return annotation != null && pathPredicate.test(annotation.value()); - functionBlockBuilder.add("$T $L = $L.$L($L);", futureType, "response", controllerName, m.getName(), controllerMethodArgs); + }); + } - } - else - { - functionBlockBuilder.add("$T $L = $L.$L($L);", m.getGenericReturnType(), "response", controllerName, m.getName(), controllerMethodArgs); - } - - methodBuilder.addCode(functionBlockBuilder.build()); - - methodBuilder.addCode("$L", "\n"); - - if (m.getReturnType().equals(ServerResponse.class)) - { - methodBuilder.addStatement("$L.send(this,$L)", "response", "exchange"); - - } - else if (m.getReturnType().getTypeName().contains("java.util.concurrent.CompletionStage") - || m.getReturnType().getTypeName().contains("java.util.concurrent.CompletableFuture")) - { - String postProcess = "."; - - if (!producesContentType.contains(",")) - { - if (producesContentType.contains(MediaType.APPLICATION_JSON)) - { - postProcess = ".applicationJson()."; - } - else if (producesContentType.contains(MediaType.APPLICATION_XML)) - { - postProcess = ".applicationXml()."; - } - else if (producesContentType.contains(MediaType.TEXT_HTML)) - { - postProcess = ".textHtml()."; - } - } - - methodBuilder.addCode( - "$L.thenAcceptAsync( r -> r" + postProcess + "send(this,$L), io.undertow.util.SameThreadExecutor.INSTANCE )\n\t.exceptionally( ex -> ", - "response", "exchange"); - methodBuilder.beginControlFlow("", ""); - methodBuilder.addCode("\t\tthrow new java.util.concurrent.CompletionException(ex);\n\t"); - methodBuilder.endControlFlow(")", ""); - } - else - { - - methodBuilder.addStatement("exchange.getResponseHeaders().put($T.CONTENT_TYPE, $S)", Headers.class, producesContentType); - - if (m.getReturnType().equals(String.class)) - { - methodBuilder.addStatement("exchange.getResponseHeaders().send($L)", "response"); - } - else - { - methodBuilder.addStatement("exchange.getResponseSender().send($L.toString())", "response"); - } - - } - - - } - else - { - - functionBlockBuilder.add("$L.$L($L);", controllerName, m.getName(), controllerMethodArgs); - - methodBuilder.addCode(functionBlockBuilder.build()); - - methodBuilder.addCode("$L", "\n"); - - - } - - if(isBlocking) - { - methodBuilder.endControlFlow("catch ($T e) { \n\texchange.putAttachment(io.sinistral.proteus.server.handlers.ServerDefaultResponseListener.EXCEPTION, e);\n\texchange.endExchange();\n}", Exception.class); - methodBuilder.endControlFlow(")", ""); - - } - - handlerClassBuilder.addMethod(methodBuilder.build()); + return stream.collect(Collectors.toSet()); + } - FieldSpec handlerField = FieldSpec.builder(httpHandlerClass, handlerName, Modifier.FINAL).initializer("$L", handlerClassBuilder.build()).build(); + protected static Type extractErasedType(Type type) { + String typeName = type.getTypeName(); - initBuilder.addCode("$L\n", handlerField.toString()); + Matcher matcher = TYPE_NAME_PATTERN.matcher(typeName); - Optional wrapAnnotation = Optional.ofNullable(m.getAnnotation(io.sinistral.proteus.annotations.Chain.class)); - - /* - * Authorization - */ - - List securityDefinitions = new ArrayList<>(); - - /* - * @TODO wrap blocking in BlockingHandler - */ - - if (Optional.ofNullable(m.getAnnotation(Path.class)).isPresent()) - { - SecurityRequirement securityRequirementAnnotation = m.getAnnotation(SecurityRequirement.class); - - if(securityRequirementAnnotation != null) - { - String securityRequirement = securityRequirementAnnotation.name(); - - securityDefinitions.add(securityRequirement); - } - - } - - if (securityDefinitions.isEmpty()) - { - securityDefinitions.addAll(typeLevelSecurityDefinitions); - } - - if (isBlocking && isDebug) - { - handlerName = "new io.undertow.server.handlers.RequestDumpingHandler(new io.undertow.server.handlers.BlockingHandler(new io.undertow.server.handlers.RequestBufferingHandler.Wrapper(8).wrap(" + handlerName + ")))"; - } - else if( isBlocking ) - { - handlerName = "new io.undertow.server.handlers.BlockingHandler(new io.undertow.server.handlers.RequestBufferingHandler.Wrapper(8).wrap(" + handlerName + "))"; - - } - else if( isDebug ) - { - handlerName = "new io.undertow.server.handlers.RequestDumpingHandler(" + handlerName + ")"; - - } - - if (wrapAnnotation.isPresent() || typeLevelHandlerWrapperMap.size() > 0 || securityDefinitions.size() > 0) - { - initBuilder.addStatement("currentHandler = $L", handlerName); - - if (wrapAnnotation.isPresent()) - { - io.sinistral.proteus.annotations.Chain w = wrapAnnotation.get(); - - Class[] wrapperClasses = w.value(); - - for(Class wrapperClass : wrapperClasses) - { - String wrapperName = typeLevelHandlerWrapperMap.get(wrapperClass); - - if (wrapperName == null) - { - wrapperName = String.format("%s_%d",generateFieldName(wrapperClass.getCanonicalName()),handlerWrapperIndex++) ; - - initBuilder.addStatement("final $T $L = new $T()", wrapperClass, wrapperName, wrapperClass); - } - - initBuilder.addStatement("currentHandler = $L.wrap($L)", wrapperName, "currentHandler"); - } - } - - for (Class wrapperClass : typeLevelHandlerWrapperMap.keySet()) - { - String wrapperName = typeLevelHandlerWrapperMap.get(wrapperClass); - initBuilder.addStatement("currentHandler = $L.wrap($L)", wrapperName, "currentHandler"); - } - - for (String securityDefinitionName : securityDefinitions) - { - initBuilder.addStatement("currentHandler = registeredHandlerWrappers.get($S).wrap($L)", securityDefinitionName, "currentHandler"); - } - - initBuilder.addStatement("$L.add(io.undertow.util.Methods.$L,$S,$L)", "router", httpMethod, methodPath, "currentHandler"); - } - else - { - initBuilder.addStatement("$L.add(io.undertow.util.Methods.$L,$S,$L)", "router", httpMethod, methodPath, handlerName); - } - - initBuilder.addCode("$L", "\n"); - - registeredEndpoints.add(endpointInfo); - - } - - initBuilder.addCode("$Lreturn router;\n", "\n"); - - typeBuilder.addMethod(initBuilder.build()); - - } - - /** - * @return the packageName - */ - public String getPackageName() - { - return packageName; - } - - /** - * @param packageName - * the packageName to set - */ - public void setPackageName(String packageName) - { - this.packageName = packageName; - } - - /** - * @return the className - */ - public String getClassName() - { - return className; - } - - /** - * @param className - * the className to set - */ - public void setClassName(String className) - { - this.className = className; - } - - protected static ArrayList getClassNamesFromPackage(String packageName) throws Exception - { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - URL packageURL; - ArrayList names = new ArrayList<>(); - - packageName = packageName.replace(".", "/"); - packageURL = classLoader.getResource(packageName); - - assert packageURL != null; - URI uri = new URI(packageURL.toString()); - File folder = new File(uri.getPath()); - // won't work with path which contains blank (%20) - // File folder = new File(packageURL.getFile()); - File[] contenuti = folder.listFiles(); - String entryName; - assert contenuti != null; - for (File actual : contenuti) - { - if (actual.isDirectory()) - { - continue; - } - - entryName = actual.getName(); - entryName = entryName.substring(0, entryName.lastIndexOf('.')); - names.add(entryName); - } - - return names; - } - - protected static Set> getApiClasses(String basePath, Predicate pathPredicate) throws Exception - { - - Reflections ref = new Reflections(basePath); - Stream> stream = ref.getTypesAnnotatedWith(Path.class).stream(); - - if (pathPredicate != null) - { - stream = stream.filter(clazz -> - { - - Path annotation = clazz.getDeclaredAnnotation(Path.class); - - return annotation != null && pathPredicate.test(annotation.value()); - - }); - } - - return stream.collect(Collectors.toSet()); - - } - - protected static Type extractErasedType(Type type) - { - String typeName = type.getTypeName(); - - Matcher matcher = TYPE_NAME_PATTERN.matcher(typeName); - - if (matcher.find()) - { - - int matches = matcher.groupCount(); - - if (matches == 2) - { - - String erasedType = matcher.group(2); - - String clearDollarType = erasedType.replaceAll("\\$", "."); - - try - { - Class clazz = Class.forName(clearDollarType); - return clazz; - - } catch (Exception e1) - { - try - { - Class clazz = Class.forName(erasedType); + if (matcher.find()) { - return clazz; - - } catch (Exception e2) - { - return type; - } - } + int matches = matcher.groupCount(); - } - else if (matches > 2) - { - String erasedType = matcher.group(3); + if (matches == 2) { - String clearDollarType = erasedType.replaceAll("\\$", "."); + String erasedType = matcher.group(2); - try - { - return Class.forName(clearDollarType); - } catch (Exception e1) - { - try - { - return Class.forName(erasedType); - } catch (Exception e2) - { - return type; - } - } - } - } + String clearDollarType = erasedType.replaceAll("\\$", "."); - return null; - } + try { + Class clazz = Class.forName(clearDollarType); + return clazz; - protected static String typeReferenceNameForParameterizedType(Type type) - { + } catch (Exception e1) { + try { + Class clazz = Class.forName(erasedType); - String typeName = type.getTypeName(); + return clazz; - if (typeName.contains("Optional")) - { - log.warn("For an optional named: " + typeName); - } + } catch (Exception e2) { + return type; + } + } - Matcher matcher = TYPE_NAME_PATTERN.matcher(typeName); + } else if (matches > 2) { + String erasedType = matcher.group(3); - if (matcher.find()) - { + String clearDollarType = erasedType.replaceAll("\\$", "."); - int matches = matcher.groupCount(); + try { + return Class.forName(clearDollarType); + } catch (Exception e1) { + try { + return Class.forName(erasedType); + } catch (Exception e2) { + return type; + } + } + } + } - if (matches == 2) - { - String genericInterface = matcher.group(1); - String erasedType = matcher.group(2).replaceAll("\\$", "."); + return null; + } - String[] genericParts = genericInterface.split("\\."); - String[] erasedParts = erasedType.split("\\."); + protected static String typeReferenceNameForParameterizedType(Type type) { - String genericTypeName = genericParts[genericParts.length - 1]; - String erasedTypeName; + String typeName = type.getTypeName(); - if (erasedParts.length > 1) - { - erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1]; - } - else - { - erasedTypeName = erasedParts[0]; - } + if (typeName.contains("Optional")) { + log.warn("For an optional named: " + typeName); + } - typeName = String.format("%s%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1, erasedTypeName.length()), genericTypeName); + Matcher matcher = TYPE_NAME_PATTERN.matcher(typeName); - return typeName; - } + if (matcher.find()) { - } + int matches = matcher.groupCount(); - matcher = CONCURRENT_TYPE_NAME_PATTERN.matcher(typeName); + if (matches == 2) { + String genericInterface = matcher.group(1); + String erasedType = matcher.group(2).replaceAll("\\$", "."); - if (matcher.find()) - { + String[] genericParts = genericInterface.split("\\."); + String[] erasedParts = erasedType.split("\\."); - int matches = matcher.groupCount(); + String genericTypeName = genericParts[genericParts.length - 1]; + String erasedTypeName; - if (matches == 2) - { - String genericInterface = matcher.group(1); - String erasedType = matcher.group(2).replaceAll("\\$", "."); + if (erasedParts.length > 1) { + erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1]; + } else { + erasedTypeName = erasedParts[0]; + } - String[] genericParts = genericInterface.split("\\."); - String[] erasedParts = erasedType.split("\\."); + typeName = String.format("%s%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1, erasedTypeName.length()), genericTypeName); - String genericTypeName = genericParts[genericParts.length - 1]; - String erasedTypeName; + return typeName; + } - if (erasedParts.length > 1) - { - erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1]; - } - else - { - erasedTypeName = erasedParts[0]; - } + } - typeName = String.format("%s%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1, erasedTypeName.length()), genericTypeName); - return typeName; - } + matcher = CONCURRENT_TYPE_NAME_PATTERN.matcher(typeName); - } + if (matcher.find()) { - return typeName; - } + int matches = matcher.groupCount(); - protected static String typeReferenceNameForType(Type type) - { - String typeName = type.getTypeName(); + if (matches == 2) { + String genericInterface = matcher.group(1); + String erasedType = matcher.group(2).replaceAll("\\$", "."); - String[] erasedParts = typeName.split("\\."); + String[] genericParts = genericInterface.split("\\."); + String[] erasedParts = erasedType.split("\\."); - String erasedTypeName; + String genericTypeName = genericParts[genericParts.length - 1]; + String erasedTypeName; + + if (erasedParts.length > 1) { + erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1]; + } else { + erasedTypeName = erasedParts[0]; + } + + typeName = String.format("%s%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1, erasedTypeName.length()), genericTypeName); + return typeName; + } - if (erasedParts.length > 1) - { - erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1]; - } - else - { - erasedTypeName = erasedParts[0]; - } + } - typeName = generateFieldName(erasedTypeName); + return typeName; + } - return typeName; - } + protected static String typeReferenceNameForType(Type type) { + String typeName = type.getTypeName(); - protected static String generateFieldName(String name) - { - String[] parts = name.split("\\."); + String[] erasedParts = typeName.split("\\."); - StringBuilder sb = new StringBuilder(); + String erasedTypeName; - for (int i = 0; i < parts.length; i++) - { - String part = parts[i]; + if (erasedParts.length > 1) { + erasedTypeName = erasedParts[erasedParts.length - 2] + erasedParts[erasedParts.length - 1]; + } else { + erasedTypeName = erasedParts[0]; + } - if (i == 0) - { - sb.append(String.format("%s%s", Character.toLowerCase(part.charAt(0)), part.substring(1, part.length()))); - } - else - { - sb.append(String.format("%s%s", Character.toUpperCase(part.charAt(0)), part.substring(1, part.length()))); - } - } + typeName = generateFieldName(erasedTypeName); + + return typeName; + } + + protected static String generateFieldName(String name) { + String[] parts = name.split("\\."); + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < parts.length; i++) { + String part = parts[i]; + + if (i == 0) { + sb.append(String.format("%s%s", Character.toLowerCase(part.charAt(0)), part.substring(1, part.length()))); + } else { + sb.append(String.format("%s%s", Character.toUpperCase(part.charAt(0)), part.substring(1, part.length()))); + } + } - return sb.toString(); - } + return sb.toString(); + } - protected static void generateTypeReference(MethodSpec.Builder builder, Type type, String name) - { + protected static void generateTypeReference(MethodSpec.Builder builder, Type type, String name) { - builder.addCode( - CodeBlock - .of("\n\ncom.fasterxml.jackson.core.type.TypeReference<$T> $L = new com.fasterxml.jackson.core.type.TypeReference<$L>(){};\n\n", type, name, type)); + builder.addCode( + CodeBlock + .of("\n\ncom.fasterxml.jackson.core.type.TypeReference<$T> $L = new com.fasterxml.jackson.core.type.TypeReference<$L>(){};\n\n", type, name, type)); - } + } - protected static void generateParameterReference(MethodSpec.Builder builder, Class clazz) - { + protected static void generateParameterReference(MethodSpec.Builder builder, Class clazz) { - builder.addCode(CodeBlock.of("\n\nType $LType = $T.", clazz, clazz)); + builder.addCode(CodeBlock.of("\n\nType $LType = $T.", clazz, clazz)); - } + } - protected static boolean hasValueOfMethod(Class clazz) - { - return Arrays.stream(clazz.getMethods()).anyMatch(m -> m.getName().equals("valueOf")); - } + protected static boolean hasValueOfMethod(Class clazz) { + return Arrays.stream(clazz.getMethods()).anyMatch(m -> m.getName().equals("valueOf")); + } - protected static boolean hasFromStringMethod(Class clazz) - { - return Arrays.stream(clazz.getMethods()).anyMatch(m -> m.getName().equals("fromString")); - } + protected static boolean hasFromStringMethod(Class clazz) { + return Arrays.stream(clazz.getMethods()).anyMatch(m -> m.getName().equals("fromString")); + } } diff --git a/src/main/java/io/sinistral/proteus/services/SwaggerService.java b/src/main/java/io/sinistral/proteus/services/SwaggerService.java index 9933522..d27032e 100644 --- a/src/main/java/io/sinistral/proteus/services/SwaggerService.java +++ b/src/main/java/io/sinistral/proteus/services/SwaggerService.java @@ -620,10 +620,4 @@ protected void startUp() throws Exception router.addAll(this.get()); } - @Override - protected void shutDown() throws Exception - { - - } - } diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 32c068d..2ae43f3 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -185,7 +185,7 @@ undertow # x AvailableProcessors ioThreadsMultiplier = 2 - workerThreads = 200 + workerThreadMultiplier = 8 bufferSize = 16k directBuffers = true } diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf index 22c446a..401fe14 100644 --- a/src/test/resources/application.conf +++ b/src/test/resources/application.conf @@ -176,7 +176,7 @@ undertow enableHttp2=false # x AvailableProcessors ioThreadsMultiplier = 2 - workerThreads = 200 + workerThreadMultiplier = 8 bufferSize = 16K directBuffers = true }