From 054892c369e65d019ca33ef41e39190d2fc78da4 Mon Sep 17 00:00:00 2001 From: Joshua Bauer Date: Tue, 5 Jul 2022 19:19:08 -0700 Subject: [PATCH] Updated dependencies and new tests. --- CHANGELOG.md | 5 + pom.xml | 1086 ++++++++--------- proteus-core/conf/logback-test.xml | 33 +- .../sinistral/proteus/ProteusApplication.java | 23 + .../sinistral/proteus/server/Extractors.java | 71 +- .../proteus/server/ServerRequest.java | 46 +- .../server/handlers/HandlerGenerator.java | 187 ++- .../proteus/server/handlers/TypeHandler.java | 8 +- .../proteus/services/AssetsService.java | 25 +- .../sinistral/proteus/utilities/DataOps.java | 4 + .../proteus/test/controllers/GenericBean.java | 9 + .../test/controllers/GenericBeanTest.java | 252 +++- .../proteus/test/controllers/Tests.java | 31 +- .../test/server/AbstractEndpointTest.java | 104 ++ .../proteus/test/server/DefaultServer.java | 4 +- .../test/server/IsolatedEndpointsTest.java | 47 +- .../test/server/StandardEndpointsTest.java | 77 +- .../test/server/UploadEndpointsTest.java | 79 +- .../src/test/resources/logback-test.xml | 10 +- .../openapi/jaxrs2/ServerModelResolver.java | 2 +- .../openapi/wrappers/HeaderApiKeyWrapper.java | 2 +- 21 files changed, 1262 insertions(+), 843 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 171e50d..c8ea43d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Proteus Changelog. ## Unreleased ### No issue +**Fix tmp file handling.** + + +[8ff44d154739bf3](https://github.com/noboomu/proteus/commit/8ff44d154739bf3) Joshua Bauer *2022-05-23 17:53:30* + **Better generic support.** diff --git a/pom.xml b/pom.xml index b2a9407..8624b5e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,555 +1,537 @@ - - 4.0.0 - - io.sinistral - proteus-project - pom - 0.5.1-SNAPSHOT - Proteus Project - - Proteus is an extremely light, fast, and flexible Java REST API framework built atop Undertow. - http://github.com/noboomu/proteus - - - - proteus-core - proteus-openapi - - - - - MIT License - http://www.opensource.org/licenses/mit-license.php - - - - - - Joshua Lee Bauer - bauer@sinistral.io - - - - - scm:git:git://github.com/noboomu/proteus.git - scm:git:ssh://github.com/noboomu/proteus.git - http://github.com/noboomu/proteus/tree/master - HEAD - - - - - 1.60 - 2.8.0 - 3.11 - 30.0-jre - 5.0.1 - 4.4.13 - 2.12.2 - 2.1.6 - 1.18 - 11 - 1.13.0 - 1.2.3 - 3.8.1 - 3.0.0-M1 - 1.6 - 3.2.0 - 3.2.0 - 2.5.3 - 3.2.1 - 3.2.1 - 3.0.0-M3 - 3.0.0-M4 - 1.6.8 - 2.1.5 - 2.21ea80 - UTF-8 - ${project.version} - 0.9.12 - 1.7.30 - 1.28 - 1.5.21 - 1.4.1 - 2.2.7.Final - 6.2.4 - 1.6.5 - - - - - - - io.rest-assured - rest-assured - 3.2.0 - test - - - - org.junit.jupiter - junit-jupiter-api - 5.3.1 - test - - - - org.junit.vintage - junit-vintage-engine - 5.3.1 - test - - - - io.micrometer - micrometer-core - ${micrometer-core.version} - - - - - - - - - - - - - com.fasterxml.jackson - jackson-bom - ${jackson.version} - import - pom - - - - io.sinistral - proteus-core - ${project.version} - - - - io.sinistral - proteus-openapi - ${project.version} - - - - io.sinistral - proteus-swagger - ${project.version} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sonatype-snapshots - https://oss.sonatype.org/content/repositories/snapshots - - true - - - false - - - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - -parameters - UTF-8 - - - - - - maven-jar-plugin - ${maven-jar-plugin.version} - - - - true - true - - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - ${maven-enforcer-plugin.version} - - - enforce-versions - - enforce - - - - - ${java.version} - - - - - - - - - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - org.apache.maven.surefire - surefire-junit47 - ${maven-surefire-plugin.version} - - - - -Dconfig.file=src/test/resources/application.conf - -Dlogback.configuration=src/test/resources/logback-test.xml - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - 8 - false - - ${maven-javadoc-plugin.version} - - - attach-javadocs - - jar - - - none - - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - gpg - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${nexus-staging-maven-plugin.version} - true - - ossrh - https://oss.sonatype.org/ - false - - - - - org.apache.maven.plugins - maven-release-plugin - ${maven-release-plugin.version} - - true - v@{project.version} - central - deploy - - - - - - - - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - 8 - - ${maven-javadoc-plugin.version} - - - attach-javadocs - - jar - - - none - - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - gpg - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${nexus-staging-plugin.version} - true - - ossrh - https://oss.sonatype.org/ - false - - - - org.apache.maven.plugins - maven-release-plugin - ${maven-release-plugin.version} - - false - v@{project.version} - ossrh - deploy - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - -parameters - - - - - org.apache.maven.plugins - maven-enforcer-plugin - - - enforce-versions - - enforce - - - - - ${java.version} - - - - - - - - maven-surefire-plugin - ${maven-surefire-plugin.version} - - - org.apache.maven.surefire - surefire-junit47 - ${maven-surefire-plugin.version} - - - - -Dconfig.file=src/test/resources/application.conf - -Dlogback.configuration=src/test/resources/logback-test.xml - - - - - - - - - - - - - - - - - - - pom.xml - - - central - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - 8 - - - - attach-javadocs - - jar - - - none - - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - gpg - - - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${nexus-staging-maven-plugin.version} - true - - ossrh - https://oss.sonatype.org/ - false - - - - - - - - - - - - - ossrh - Sonatype Nexus Snapshots - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - Nexus Release Repository - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - https://oss.sonatype.org/content/groups/public/io/sinistral/proteus-project - - + + 4.0.0 + + io.sinistral + proteus-project + pom + 0.5.1-SNAPSHOT + Proteus Project + + Proteus is an extremely light, fast, and flexible Java REST API framework built atop Undertow. + http://github.com/noboomu/proteus + + + proteus-core + proteus-openapi + + + + + MIT License + http://www.opensource.org/licenses/mit-license.php + + + + + + Joshua Lee Bauer + bauer@sinistral.io + + + + + scm:git:git://github.com/noboomu/proteus.git + scm:git:ssh://github.com/noboomu/proteus.git + http://github.com/noboomu/proteus/tree/master + HEAD + + + + + 1.60 + 2.11.0 + 3.12.0 + 31.1-jre + 5.1.0 + 4.4.15 + 2.13.3 + 2.1.6 + 1.18 + 11 + 1.13.0 + 1.2.11 + 3.8.1 + 3.0.0-M1 + 1.6 + 3.2.0 + 3.2.0 + 2.5.3 + 3.2.1 + 3.2.1 + 3.0.0-M3 + 3.0.0-M4 + 1.6.8 + 2.2.1 + 2.21ea82 + UTF-8 + ${project.version} + 0.10.2 + 1.7.36 + 1.30 + 1.5.21 + 1.4.2 + 2.2.18.Final + 6.3.0 + 1.9.1 + + + + + + org.junit.jupiter + junit-jupiter-api + 5.8.2 + test + + + + org.junit.vintage + junit-vintage-engine + 5.8.2 + test + + + + io.rest-assured + rest-assured + 3.2.0 + test + + + + io.micrometer + micrometer-core + ${micrometer-core.version} + + + + + + + + + com.fasterxml.jackson + jackson-bom + ${jackson.version} + import + pom + + + + io.sinistral + proteus-core + ${project.version} + + + + io.sinistral + proteus-openapi + ${project.version} + + + + io.sinistral + proteus-swagger + ${project.version} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sonatype-snapshots + https://oss.sonatype.org/content/repositories/snapshots + + true + + + false + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + -parameters + UTF-8 + + + + + maven-jar-plugin + ${maven-jar-plugin.version} + + + + true + true + + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-versions + + enforce + + + + + ${java.version} + + + + + + + + + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.apache.maven.surefire + surefire-junit47 + ${maven-surefire-plugin.version} + + + + -Dconfig.file=src/test/resources/application.conf + -Dlogback.configuration=src/test/resources/logback-test.xml + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + 8 + false + + ${maven-javadoc-plugin.version} + + + attach-javadocs + + jar + + + none + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + gpg + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus-staging-maven-plugin.version} + true + + ossrh + https://oss.sonatype.org/ + false + + + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + true + v@{project.version} + central + deploy + + + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + 8 + + ${maven-javadoc-plugin.version} + + + attach-javadocs + + jar + + + none + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + gpg + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus-staging-plugin.version} + true + + ossrh + https://oss.sonatype.org/ + false + + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + false + v@{project.version} + ossrh + deploy + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + -parameters + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-versions + + enforce + + + + + ${java.version} + + + + + + + + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.apache.maven.surefire + surefire-junit47 + ${maven-surefire-plugin.version} + + + + -Dconfig.file=src/test/resources/application.conf + -Dlogback.configuration=src/test/resources/logback-test.xml + + + + + + + + + + + + + + + pom.xml + + + central + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + 8 + + + + attach-javadocs + + jar + + + none + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + gpg + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus-staging-maven-plugin.version} + true + + ossrh + https://oss.sonatype.org/ + false + + + + + + + + + + + ossrh + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + https://oss.sonatype.org/content/groups/public/io/sinistral/proteus-project + + \ No newline at end of file diff --git a/proteus-core/conf/logback-test.xml b/proteus-core/conf/logback-test.xml index 16539fa..2fb2167 100644 --- a/proteus-core/conf/logback-test.xml +++ b/proteus-core/conf/logback-test.xml @@ -1,52 +1,49 @@ - - - + true - %date{ISO8601} %highlight(%-5level) [%boldCyan(%logger)] [%boldYellow(%method %F) ] - %boldWhite(%message) %n %red(%ex) - - + %date{ISO8601} [%thread] %highlight(%-5level) [%boldCyan(%logger{36})] [%boldYellow(%method %F) %line] - %boldWhite(%message) %n %red(%rEx) %nopex + + - + - - + + - + - + - + - + - - - - + + + + \ No newline at end of file diff --git a/proteus-core/src/main/java/io/sinistral/proteus/ProteusApplication.java b/proteus-core/src/main/java/io/sinistral/proteus/ProteusApplication.java index 81cf0bb..c3538ca 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/ProteusApplication.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/ProteusApplication.java @@ -33,6 +33,9 @@ import org.apache.commons.lang3.time.DurationFormatUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.xnio.Xnio; +import org.xnio.XnioWorker; + import javax.ws.rs.core.MediaType; import java.io.ByteArrayOutputStream; import java.io.File; @@ -667,6 +670,26 @@ public List getPorts() return ports; } + public void stop() { + + undertow.stop(); + } + + public Xnio getXnio() { + + return undertow.getXnio(); + } + + public XnioWorker getWorker() { + + return undertow.getWorker(); + } + + public List getListenerInfo() { + + return undertow.getListenerInfo(); + } + /** * @return The Undertow server */ diff --git a/proteus-core/src/main/java/io/sinistral/proteus/server/Extractors.java b/proteus-core/src/main/java/io/sinistral/proteus/server/Extractors.java index 60eba9e..e9fd790 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/server/Extractors.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/server/Extractors.java @@ -37,7 +37,7 @@ import java.util.Date; import java.util.Deque; import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -55,6 +55,7 @@ public class Extractors { private static Logger log = LoggerFactory.getLogger(Extractors.class.getCanonicalName()); private final static Pattern XML_PATTERN = Pattern.compile("^(application/(xml|xhtml\\+xml)|text/xml)(;.*)?$", Pattern.CASE_INSENSITIVE); + private final static Pattern JSON_PATTERN = Pattern.compile("^(application/(json|x-javascript)|text/(json|x-javascript|x-json))(;.*)?$", Pattern.CASE_INSENSITIVE); private static final Map JAVA_TYPE_MAP = new ConcurrentHashMap<>(); @@ -190,20 +191,57 @@ private static java.util.Optional> formValueFilePaths(final HttpSer private static java.util.Optional> formValuePathMap(final HttpServerExchange exchange, final String name) { - return java.util.Optional.ofNullable(exchange.getAttachment(FormDataParser.FORM_DATA).get(name)).map(deque -> deque.stream().filter(fv -> fv.getFileItem() != null).collect(Collectors.toMap(FormData.FormValue::getFileName, fv -> formValueFileItemPath(fv.getFileItem())))); + return java.util.Optional.ofNullable(exchange.getAttachment(FormDataParser.FORM_DATA).get(name)) + .map(deque -> deque.stream().filter(fv -> fv.getFileItem() != null).collect(Collectors.toMap(FormData.FormValue::getFileName, fv -> formValueFileItemPath(fv.getFileItem())))); } private static java.util.Optional> formValueFileMap(final HttpServerExchange exchange, final String name) { - return java.util.Optional.ofNullable(exchange.getAttachment(FormDataParser.FORM_DATA).get(name)).map(deque -> deque.stream().filter(fv -> fv.getFileItem() != null).collect(Collectors.toMap(FormData.FormValue::getFileName, fv -> formValueFileItemPath(fv.getFileItem()).toFile()))); + return java.util.Optional.ofNullable(exchange.getAttachment(FormDataParser.FORM_DATA).get(name)) + .map(deque -> deque.stream().filter(fv -> fv.getFileItem() != null).collect(Collectors.toMap(FormData.FormValue::getFileName, fv -> formValueFileItemPath(fv.getFileItem()).toFile()))); } private static java.util.Optional formValueBuffer(final HttpServerExchange exchange, final String name) { - return java.util.Optional.ofNullable(exchange.getAttachment(FormDataParser.FORM_DATA).get(name)).map(Deque::getFirst).map(fi -> { + FormData formData = exchange.getAttachment(FormDataParser.FORM_DATA); + + // + + Iterator itr = formData.iterator(); + + while(itr.hasNext()) + { + String s = itr.next(); + + // + + Deque deque = formData.get(s); + + var values = deque.stream().map(v -> { + + + if(v.isFileItem()) + { + return Map.of("headers",v.getHeaders(),"fileItem",v.getFileItem(),"fileName",v.getFileName()); + } + else + + { + return Map.of("headers",v.getHeaders(),"value",v.getValue()); + } + + + }).collect(Collectors.toList()); + + // + + } + + + return java.util.Optional.ofNullable(formData.get(name)).map(Deque::getFirst).map(fi -> { try { @@ -212,12 +250,18 @@ private static java.util.Optional formValueBuffer(final HttpServerEx { FormData.FileItem fileItem = fi.getFileItem(); + log.trace("fileItem: {} {} ",fileItem,fileItem.getFile()); + + if(fileItem.isInMemory()) + { + log.trace("fileItem: {} is in memory {}", fileItem, fileItem.getFileSize()); + } return DataOps.fileItemToBuffer(fileItem); } } catch (Exception e) { - log.error("Failed to parse buffer for name {}", name); + log.error("Failed to parse buffer for name {}", name, e); } return null; @@ -300,7 +344,7 @@ else if (formValue.getHeaders().get(Headers.CONTENT_TYPE) == null || (formValue. } else { - log.warn("Unable to find suitable content for {}", name); + log.warn("FormValue for {} is not a file item", name); return null; } } @@ -359,7 +403,7 @@ else if (formValue.getHeaders().get(Headers.CONTENT_TYPE) == null || (formValue. } else { - log.warn("Unable to find suitable content for {}", name); + log.warn("FormValue for {} is not a file item", name); return null; } } @@ -558,8 +602,6 @@ public static java.util.Optional file(final HttpServerExchange exchange, f return formValueFilePath(exchange, name).map(Path::toFile); } - - public static java.util.Optional byteBuffer(final HttpServerExchange exchange) throws IOException { @@ -596,6 +638,7 @@ public static OffsetDateTime offsetDateTime(final HttpServerExchange exchange, f public static Path filePath(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { + return formValueFilePath(exchange, name).orElseThrow(() -> new IllegalArgumentException("Invalid parameter " + name)); } @@ -613,18 +656,20 @@ public static List fileList(final HttpServerExchange exchange, final Strin public static Map pathMap(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { - return formValuePathMap(exchange,name).orElse(new HashMap<>()); + + return formValuePathMap(exchange, name).orElse(new HashMap<>()); } public static Map fileMap(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { - return formValueFileMap(exchange,name).orElse(new HashMap<>()); + return formValueFileMap(exchange, name).orElse(new HashMap<>()); } public static File file(final HttpServerExchange exchange, final String name) throws IllegalArgumentException { - return formValueFilePath(exchange,name).map(Path::toFile).orElseThrow(() -> new IllegalArgumentException("Invalid parameter " + name)); + + return formValueFilePath(exchange, name).map(Path::toFile).orElseThrow(() -> new IllegalArgumentException("Invalid parameter " + name)); } public static ByteBuffer byteBuffer(final HttpServerExchange exchange) throws IOException @@ -743,11 +788,13 @@ public static JsonNode any(final HttpServerExchange exchange) public static JsonNode jsonNode(final HttpServerExchange exchange) { + return parseJson(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array()); } public static JsonNode namedJsonNode(final HttpServerExchange exchange, final String name) { + return formValue(exchange, name).map(fv -> formValueModel(fv, JsonNode.class, name)).orElseThrow(() -> new IllegalArgumentException("Invalid parameter " + name)); } diff --git a/proteus-core/src/main/java/io/sinistral/proteus/server/ServerRequest.java b/proteus-core/src/main/java/io/sinistral/proteus/server/ServerRequest.java index a4b2087..55f968d 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/server/ServerRequest.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/server/ServerRequest.java @@ -48,6 +48,7 @@ import java.io.OutputStream; import java.net.InetSocketAddress; import java.nio.ByteBuffer; +import java.nio.file.Path; import java.util.Deque; import java.util.Iterator; import java.util.List; @@ -60,9 +61,7 @@ import static io.undertow.server.handlers.form.FormDataParser.FORM_DATA; /** - * * @author jbauer - * */ public class ServerRequest { @@ -70,6 +69,9 @@ public class ServerRequest { public static final AttachmentKey BYTE_BUFFER_KEY = AttachmentKey.create(ByteBuffer.class); + + public static final AttachmentKey SERVER_REQUEST_ATTACHMENT_KEY = AttachmentKey.create(ServerRequest.class); + protected static final Receiver.ErrorCallback ERROR_CALLBACK = (exchange, e) -> { exchange.putAttachment(ExceptionHandler.THROWABLE, e); exchange.endExchange(); @@ -78,9 +80,13 @@ public class ServerRequest { protected static final String TMP_DIR = System.getProperty("java.io.tmpdir"); public final HttpServerExchange exchange; + protected final String path; + protected final String contentType; + protected final String method; + protected final String accept; public ServerRequest() @@ -124,11 +130,13 @@ else if (exchange.getRequestContentLength() > 0) public String accept() { + return this.accept; } public String contentType() { + return this.contentType; } @@ -138,9 +146,9 @@ public HttpServerExchange exchange() return exchange; } - public Deque files(final String name) { + FormData formData = this.exchange.getAttachment(FORM_DATA); if (formData != null) @@ -183,6 +191,7 @@ public void startAsync(final Executor executor, final Runnable runnable) /** * Abort current request and respond with 302 redirect. Returns empty @ServerResponse for convenience. + * * @param location * @param includeParameters * @return serverResponse @@ -200,6 +209,7 @@ public ServerResponse redirect(String location, boolean includeParameters /** * Abort current request and respond with 301 redirect. Returns empty @ServerResponse for convenience. + * * @param location * @param includeParameters * @return serverResponse @@ -257,6 +267,7 @@ public Map> getQueryParameters() */ public SecurityContext getSecurityContext() { + return exchange.getSecurityContext(); } @@ -789,6 +800,7 @@ public void addToAttachmentList(AttachmentKey> key, T valu private void extractFormParameters(final FormData formData) { + if (formData != null) { for (String key : formData) @@ -811,23 +823,29 @@ private void extractFormParameters(final FormData formData) private void parseEncodedForm() throws IOException { - this.exchange.startBlocking(); - - final FormDataParser formDataParser = new FormEncodedDataDefinition().setDefaultEncoding(this.exchange.getRequestCharset()).create(exchange); - - if(formDataParser != null) + try (BlockingHttpExchange blockingHttpExchange = this.exchange.startBlocking()) { - final FormData formData = formDataParser.parseBlocking(); - extractFormParameters(formData); + try (FormDataParser formDataParser = new FormEncodedDataDefinition().setDefaultEncoding(this.exchange.getRequestCharset()).create(exchange)) + { + if (formDataParser != null) + { + final FormData formData = formDataParser.parseBlocking(); + extractFormParameters(formData); + } + } } } private void parseMultipartForm() throws IOException { - this.exchange.startBlocking(); + + final String charset = exchange.getRequestCharset(); + + this.exchange.startBlocking(); + final MultiPartParserDefinition multiPartParserDefinition = new MultiPartParserDefinition() - .setTempFileLocation(new File(TMP_DIR).toPath()).setDefaultEncoding(this.exchange.getRequestCharset()); + .setTempFileLocation(Path.of(TMP_DIR)).setDefaultEncoding(charset); final long thresholdSize = exchange.getConnection().getUndertowOptions().get(UndertowOptions.MAX_BUFFERED_REQUEST_SIZE, 0); @@ -839,9 +857,13 @@ private void parseMultipartForm() throws IOException { final FormData formData = formDataParser.parseBlocking(); extractFormParameters(formData); + } + + } + } diff --git a/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java b/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java index f31b6bc..8675edc 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java @@ -4,7 +4,9 @@ package io.sinistral.proteus.server.handlers; import com.fasterxml.jackson.core.type.TypeReference; -import com.google.common.base.Strings; +import com.google.common.reflect.Invokable; +import com.google.common.reflect.MutableTypeToInstanceMap; +import com.google.common.reflect.TypeToken; import com.google.inject.Inject; import com.google.inject.name.Named; import com.squareup.javapoet.AnnotationSpec; @@ -61,8 +63,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.regex.Matcher; @@ -77,10 +81,13 @@ * * @author jbauer */ +@SuppressWarnings("UnstableApiUsage") public class HandlerGenerator { static Logger log = LoggerFactory.getLogger(HandlerGenerator.class.getCanonicalName()); + static final java.util.regex.Pattern IGNORED_TYPE_NAME_PATTERN = java.util.regex.Pattern.compile("java\\.(util|net|lang|nio|io)", Pattern.CASE_INSENSITIVE); + 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); @@ -122,6 +129,8 @@ public enum StatementParameterType { protected Map, String> typeLevelHandlerWrapperMap = new LinkedHashMap<>(); + final Map> typeTokenMap = new ConcurrentHashMap<>(); + /** * Create a new {@code HandlerGenerator} instance used to generate a * {@code Supplier} class @@ -143,7 +152,7 @@ public HandlerGenerator(String packageName, Class controllerClass) * * @return a new {@code Supplier} class */ - public Class> compileClass() throws Exception + public Class compileClass() throws Exception { String source = null; @@ -158,11 +167,13 @@ public Class> compileClass() throws Exception source = this.generateClassSource(); - log.debug("\n\nGenerated Class Source:\n\n{}", source); + // log.debug("\n\nGenerated Class Source:\n\n{}", source); - CachedCompiler cachedCompiler = new CachedCompiler(null, null); + try (CachedCompiler cachedCompiler = new CachedCompiler(null, null)) + { - return cachedCompiler.loadFromJava(packageName + "." + className, source); + return cachedCompiler.loadFromJava(packageName + "." + className, source); + } } catch (Exception e) { @@ -243,15 +254,14 @@ protected java.nio.file.Path getTemporaryDirectoryPath() throws Exception try { - return Files.createDirectory(tmpPath.resolve(TMP_DIRECTORY_NAME)); + return Files.createDirectory(tmpPath.resolve(TMP_DIRECTORY_NAME)); } catch (Exception e) { return tmpPath; } - } -} + } protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class clazz) throws Exception { @@ -275,11 +285,45 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class cla .distinct().filter(t -> { + TypeHandler handler = TypeHandler.forType(t); return (handler.equals(TypeHandler.ModelType) || handler.equals(TypeHandler.OptionalModelType) || handler.equals(TypeHandler.NamedModelType) || handler.equals(TypeHandler.OptionalNamedModelType)); }).collect(Collectors.toMap(java.util.function.Function.identity(), HandlerGenerator::typeReferenceNameForParameterizedType)); + + java.util.regex.Pattern internalTypesPattern = java.util.regex.Pattern.compile("concurrent|<"); + + final Map> googleParameterTypeTokens = Arrays.stream(clazz.getDeclaredMethods()) + .filter(m -> m.getAnnotation(Path.class) != null) + .flatMap( + m -> Invokable.from(m).getParameters().stream()) + .filter(p -> internalTypesPattern.matcher(p.getType().getType().getTypeName()).find() ) + .distinct().collect(Collectors.toMap(p -> p.getType().toString(), com.google.common.reflect.Parameter::getType)); + + + +log.info("googleParameterTypeTokens: {}",googleParameterTypeTokens); + Arrays.stream(clazz.getDeclaredMethods()) + .filter(m -> m.getAnnotation(Path.class) != null) + .forEach(m -> { + Invokable invokable = Invokable.from(m); + + List> parameterTokens = invokable.getParameters().stream().filter(p -> Objects.isNull(p.getAnnotation(BeanParam.class))).map(com.google.common.reflect.Parameter::getType).collect(Collectors.toList()); + + log.info("parameterTokens: {}",parameterTokens); + + parameterTokens.forEach(rt -> { + log.info("t:\n|{}|\n|{}|\n|{}|\ncached:\n|{}|",rt.getType(), rt.getRawType(), rt, typeTokenMap.get(rt.getType())); + typeTokenMap.put(rt.getType(),rt); + }); + + // log.info("invokable: \ntype {}\n rawt {}\n hc {}\n params{}\n return type{}\n generic string {}\nog return type: {}",returnType.getType(),returnType.getRawType(),returnType.hashCode(), +// m.getParameters(),m.getReturnType(),m.getReturnType().toGenericString(), +// m.getReturnType()); + // typeTokenMap.put(m.getReturnType().getTypeName(),invokable.getReturnType()); + }); + Arrays.stream(clazz.getDeclaredMethods()) .filter(m -> m.getAnnotation(Path.class) != null) .flatMap(m -> Arrays.stream(m.getParameters())) @@ -292,6 +336,9 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class cla if (isBeanParameter) { + + // typeTokenMap.put(p.getParameterizedType(),) + TypeHandler handler = TypeHandler.forType(p.getParameterizedType(), true); if (handler.equals(TypeHandler.BeanListValueOfType) @@ -330,22 +377,11 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class cla } - 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")) + if (isIgnoredClass(t)) { return false; } + else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) { return false; @@ -414,30 +450,21 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) Annotation[] annotations = clazz.getAnnotations(); - Annotation securityRequirementAnnotation = Arrays.stream(annotations).filter(a -> a.getClass().getName().contains("SecurityRequirement" + - "")).findFirst().orElse(null); + Annotation securityRequirementAnnotation = Arrays.stream(annotations).filter(a -> a.getClass().getName().contains("SecurityRequirement")).findFirst().orElse(null); if (securityRequirementAnnotation != null) { - if (securityRequirementAnnotation != null) + try { + Field nameField = securityRequirementAnnotation.getClass().getField("name"); - try - { - Field nameField = securityRequirementAnnotation.getClass().getField("name"); - - if (nameField != null) - { - Object securityRequirement = nameField.get(securityRequirementAnnotation); - typeLevelSecurityDefinitions.add(securityRequirement.toString()); - } - - } catch (Exception e) - { - log.warn("No name field on security requirement"); - } + Object securityRequirement = nameField.get(securityRequirementAnnotation); + typeLevelSecurityDefinitions.add(securityRequirement.toString()); + } catch (Exception e) + { + log.warn("No name field on security requirement"); } } @@ -481,7 +508,7 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) Optional producesAnnotation = Optional.ofNullable(m.getAnnotation(javax.ws.rs.Produces.class)); - if (!producesAnnotation.isPresent()) + if (producesAnnotation.isEmpty()) { producesAnnotation = Optional.ofNullable(clazz.getAnnotation(javax.ws.rs.Produces.class)); @@ -506,7 +533,7 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) Optional consumesAnnotation = Optional.ofNullable(m.getAnnotation(javax.ws.rs.Consumes.class)); - if (!consumesAnnotation.isPresent()) + if (consumesAnnotation.isEmpty()) { consumesAnnotation = Optional.ofNullable(clazz.getAnnotation(javax.ws.rs.Consumes.class)); @@ -581,6 +608,8 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) .addAnnotation(Override.class) .addParameter(ParameterSpec.builder(HttpServerExchange.class, "exchange", Modifier.FINAL).build()); +// MutableTypeToInstanceMap mutableTypeToInstanceMap = new MutableTypeToInstanceMap(); + for (Parameter p : m.getParameters()) { @@ -597,6 +626,14 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) boolean isBeanParameter = beanParam != null; + Invokable invokable = Invokable.from(m); + + TypeToken token = invokable.getReturnType(); + + // Object obj - MutableTypeToInstanceMap + log.error("parameterized: {} \ntoken: {}\ntoken type: {}", p.getParameterizedType(), token, token.getType()); + + TypeHandler t = TypeHandler.forType(p.getParameterizedType(), isBeanParameter); if (t.isBlocking()) @@ -612,8 +649,6 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) } } - log.debug("parameterizedLiteralsNameMap: " + parameterizedLiteralsNameMap); - if (isBlocking) { @@ -629,6 +664,8 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class)) List parameters = Arrays.stream(m.getParameters()).collect(Collectors.toList()); + log.debug("parameterizedLiteralsNameMap: " + parameterizedLiteralsNameMap); + for (Parameter p : parameters) { @@ -1045,11 +1082,8 @@ else if (producesContentType != null) { Field nameField = securityRequirementAnnotation.getClass().getField("name"); - if (nameField != null) - { - Object securityRequirement = nameField.get(securityRequirementAnnotation); - securityDefinitions.add(securityRequirement.toString()); - } + Object securityRequirement = nameField.get(securityRequirementAnnotation); + securityDefinitions.add(securityRequirement.toString()); } catch (Exception e) { @@ -1238,7 +1272,7 @@ protected static Set> getApiClasses(String basePath, Predicate } - protected static Type extractErasedType(Type type) + public static Type extractErasedType(Type type) { String typeName = type.getTypeName(); @@ -1300,9 +1334,10 @@ else if (matches > 2) return null; } - protected static String typeReferenceNameForParameterizedType(Type type) + public static String typeReferenceNameForParameterizedType(Type type) { + log.info("creating name for reference: {}", type); String typeName = type.getTypeName(); if (typeName.contains("Optional")) @@ -1377,20 +1412,38 @@ protected static String typeReferenceNameForParameterizedType(Type type) } -// if(type instanceof ParameterizedType) -// { -// ParameterizedType pType = (ParameterizedType) type; -// Class genericType = (Class)pType.getActualTypeArguments()[0]; -// Class rawType = (Class)pType.getRawType(); -// Class erasedType = (Class) HandlerGenerator.extractErasedType(genericType); -// -// if(!(pType.getRawType() instanceof ParameterizedType)) -// { -// return Character.toLowerCase(rawType.getSimpleName().charAt(0)) + rawType.getSimpleName().substring(1) + genericType.getSimpleName(); -// } -// -// -// } + if (type.getTypeName().startsWith("sun")) + { + return typeName; + } + + if (type instanceof ParameterizedType) + { + ParameterizedType pType = (ParameterizedType) type; + log.debug("pType: {}", pType); + + Type actualTypeArgument0 = pType.getActualTypeArguments()[0]; + + if (actualTypeArgument0 instanceof Class) + { + Class genericType = (Class) pType.getActualTypeArguments()[0]; + Class rawType = (Class) pType.getRawType(); + Class erasedType = (Class) HandlerGenerator.extractErasedType(genericType); + + if (!(pType.getRawType() instanceof ParameterizedType)) + { + log.info("not a raw type that is parameterized {} {}", rawType, genericType); + return Character.toLowerCase(rawType.getSimpleName().charAt(0)) + rawType.getSimpleName().substring(1) + genericType.getSimpleName(); + } + } + else + { + log.error( + "failed to process {} ptype: {}", type, pType + ); + } + + } return typeName; } @@ -1442,6 +1495,14 @@ protected static String generateFieldName(String name) return sb.toString(); } + static boolean isIgnoredClass(Type type) + { + + java.util.regex.Matcher m = IGNORED_TYPE_NAME_PATTERN.matcher(type.getTypeName()); + + return m.find(); + } + protected static void generateTypeReference(MethodSpec.Builder builder, Type type, String name) { diff --git a/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/TypeHandler.java b/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/TypeHandler.java index aed9109..cfd4d34 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/TypeHandler.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/server/handlers/TypeHandler.java @@ -378,9 +378,9 @@ public static TypeHandler forType(Type type, Boolean isBeanParam) boolean isArray = type.getTypeName().contains("java.util.List"); boolean isSet = type.getTypeName().contains("java.util.Set"); boolean isMap = type.getTypeName().contains("java.util.Map"); - // boolean isParameterized = type instanceof ParameterizedType; + boolean isParameterized = type instanceof ParameterizedType; - if (!isOptional && !isArray && !isSet && !isMap ) + if (!isOptional && !isArray && !isSet && !isMap && !isParameterized) { try { @@ -394,7 +394,7 @@ public static TypeHandler forType(Type type, Boolean isBeanParam) } catch (Exception e) { - HandlerGenerator.log.error(e.getMessage(), e); + HandlerGenerator.log.error("failed to get class {}", type.getTypeName() , e); } } @@ -445,7 +445,7 @@ else if (erasedType.getTypeName().contains("java.io.File")) } catch (Exception e) { - HandlerGenerator.log.error(e.getMessage(), e); + log.error(e.getMessage(), e); throw e; } } diff --git a/proteus-core/src/main/java/io/sinistral/proteus/services/AssetsService.java b/proteus-core/src/main/java/io/sinistral/proteus/services/AssetsService.java index e669365..976808c 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/services/AssetsService.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/services/AssetsService.java @@ -10,7 +10,11 @@ import io.undertow.server.handlers.resource.FileResourceManager; import io.undertow.server.handlers.resource.ResourceHandler; import io.undertow.util.Methods; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.File; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; import java.util.function.Supplier; @@ -23,6 +27,9 @@ @Singleton public class AssetsService extends DefaultService implements Supplier { + + private static final Logger log = LoggerFactory.getLogger(AssetsService.class.getName()); + @Inject @Named("registeredEndpoints") protected Set registeredEndpoints; @@ -52,7 +59,23 @@ public RoutingHandler get() final String assetsPath = serviceConfig.getString("path"); final String assetsDirectoryName = serviceConfig.getString("dir"); final Integer assetsCacheTime = serviceConfig.getInt("cache.time"); - final FileResourceManager fileResourceManager = new FileResourceManager(Paths.get(assetsDirectoryName).toFile()); + + Path path = Paths.get(assetsDirectoryName); + File pathFile = path.toFile(); + + if(!pathFile.exists()) + { + try + { + pathFile.mkdirs(); + + } catch( Exception e ) + { + log.error("Failed to create assets directory",e); + } + } + + final FileResourceManager fileResourceManager = new FileResourceManager(pathFile); router.add(Methods.GET, assetsPath + "/*", diff --git a/proteus-core/src/main/java/io/sinistral/proteus/utilities/DataOps.java b/proteus-core/src/main/java/io/sinistral/proteus/utilities/DataOps.java index 5df8801..fb656e3 100644 --- a/proteus-core/src/main/java/io/sinistral/proteus/utilities/DataOps.java +++ b/proteus-core/src/main/java/io/sinistral/proteus/utilities/DataOps.java @@ -1,6 +1,8 @@ package io.sinistral.proteus.utilities; import io.undertow.server.handlers.form.FormData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; @@ -14,6 +16,8 @@ public class DataOps { + private static final Logger logger = LoggerFactory.getLogger(DataOps.class.getName()); + public static void writeStreamToPath(InputStream inputStream, Path path) throws IOException { diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBean.java b/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBean.java index 1393fa9..4af63f4 100644 --- a/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBean.java +++ b/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBean.java @@ -4,6 +4,15 @@ public class GenericBean { private S value; + public GenericBean() { + + } + + public GenericBean(S value) { + + this.value = value; + } + public S getValue() { return value; diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBeanTest.java b/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBeanTest.java index 32a9083..5f5157b 100644 --- a/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBeanTest.java +++ b/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/GenericBeanTest.java @@ -1,28 +1,264 @@ package io.sinistral.proteus.test.controllers; -import static org.junit.jupiter.api.Assertions.*; - -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeBindings; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeVariableName; +import io.sinistral.proteus.server.handlers.HandlerGenerator; +import org.apache.commons.lang3.reflect.TypeUtils; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.reflections.ReflectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.stream.Collectors; + @TestInstance(TestInstance.Lifecycle.PER_CLASS) class GenericBeanTest { private static final Logger logger = LoggerFactory.getLogger(GenericBean.class.getName()); - @Override + static ObjectMapper objectMapper = new ObjectMapper(); + + public static class GenericClass { + + enum Range { + far, close; + } + + public io.sinistral.proteus.test.controllers.GenericBean bean = new io.sinistral.proteus.test.controllers.GenericBean<>(); + + private Range range = Range.far; + + public Range getRange() { + + return range; + } + + public void setRange(Range range) { + + this.range = range; + } + + public GenericClass() { + + } + + public GenericClass(GenericBean bean) { + + this.bean = bean; + } + + public GenericBean getBean() { + + return bean; + } + + public GenericBean getLongBean() { + + return new GenericBean<>(10L); + } + + public void setBean(GenericBean bean) { + + this.bean = bean; + } + + public List> getGenericClassBean(Collection items) + { + + return items.stream().map(GenericBean::new).collect(Collectors.toList()); + } + + public List>> getComplex() + { + + Random random = new Random(); + + return List.of(Map.of("a", random.longs(5).boxed().collect(Collectors.toSet())), Map.of("b", random.longs(15).boxed().collect(Collectors.toSet()))); + + } + + public Optional getOptionalBean() + { + + return Optional.ofNullable(this.bean).map(GenericBean::getValue); + } + + } + @BeforeAll protected void setUp() { - super.setUp(); + } + + public static Class resolveGenericType(Class declaredClass) { + + if (declaredClass.isAssignableFrom(ParameterizedType.class)) + { + ParameterizedType parameterizedType = (ParameterizedType) declaredClass.getGenericSuperclass(); + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + return (Class) actualTypeArguments[0]; + } + else + { + return null; + } + } + + private JavaType constructJavaType(final Type type) { + + if (type instanceof ParameterizedType) + { + JavaType[] javaTypeArgs = new JavaType[((ParameterizedType) type).getActualTypeArguments().length]; + for (int i = 0; i != ((ParameterizedType) type).getActualTypeArguments().length; ++i) + { + javaTypeArgs[i] = constructJavaType(((ParameterizedType) type).getActualTypeArguments()[i]); + } + return objectMapper.getTypeFactory().constructType(type, + TypeBindings.create((Class) ((ParameterizedType) type).getRawType(), javaTypeArgs)); + } + else + { + return objectMapper.getTypeFactory().constructType(type); + } + } + + public JavaType generateTypeReference(Class type) + { + + return constructJavaType(type); + + } + + @Test + void genericTypeHandler() throws Exception + { + + Set methods = ReflectionUtils.get(ReflectionUtils.Methods.get(GenericClass.class)); + + for (Method m : methods) + { + + // get rawType (container) + // getActualTypeArguments (inside brackets) by name + + // method sdignature is string version + /** + * method.genericInfo + * returnType + * + */ + + logger.info("method: {} {}", m, m.getName()); + if (m.getName().contains("get")) + { + Class genericReturnType = m.getReturnType(); + + Class genericType = resolveGenericType(genericReturnType); + + // + + Type type = m.getGenericReturnType(); +String s = ""; + if (type instanceof ParameterizedType) + { + s = HandlerGenerator.typeReferenceNameForParameterizedType((ParameterizedType) type); + + + + // + } + + + + Type erasedType = HandlerGenerator.extractErasedType(type); + + // + + logger.info("generateTypeReference: {}", generateTypeReference(m.getReturnType())); + + + + Map, Type> typeArgs = new HashMap<>(); + + if(type instanceof ParameterizedType) + { + typeArgs = TypeUtils.getTypeArguments((ParameterizedType)type); + } + + MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("someMethod"); + + methodSpecBuilder.addTypeVariables(typeArgs.keySet().stream().map(TypeVariableName::get).collect(Collectors.toList())); + + methodSpecBuilder.addStatement("$T $L = new $T();",m.getGenericReturnType(),s, m.getGenericReturnType() ); + + + logger.info("args: {}\nmethod: {}",typeArgs,methodSpecBuilder.build().toString()); + StringBuilder sb1 = new StringBuilder(); + sb1.append("\n").append("type: ").append(type).append(" => ").append(genericReturnType.getTypeName()).append("\n"); + typeArgs.forEach((k, v) -> { + sb1.append("name: ").append(k.getName()).append(" typeName: ").append(k.getTypeName()).append(" bounds: ").append(Arrays.toString(k.getBounds())).append("\n"); + sb1.append("value: ").append(v).append(" typeName: ").append(v.getTypeName()).append("\n"); + }); + + logger.info("{}", sb1.toString()); + + + Class clazz = TypeUtils.getRawType(type, type); + + + Type superclass = genericReturnType.getGenericSuperclass(); + List> genericParameterTypes = Arrays.stream(genericReturnType.getTypeParameters()).collect(Collectors.toList()); + + StringBuilder sb = new StringBuilder().append("\n").append("generic return type: ").append(genericReturnType).append("\t").append(genericReturnType.getTypeName()).append("\n") + .append("getGenericReturnType:\t").append(m.getGenericReturnType()).append("\n") + .append("type parameters: ").append(genericParameterTypes); + + logger.info(sb.toString()); + sb = new StringBuilder(); + + for (TypeVariable t : genericParameterTypes) + { + + Class t1 = TypeUtils.getRawType(t, type); + + Class t2 = TypeUtils.getRawType(type, type); + + sb.append("type variable: ").append(t).append("\n").append("t: ").append(t).append("\ntype: ").append(type).append("\n").append("t1: ").append(t1).append("\n").append("t2: ").append(t2); + + logger.info(sb.toString()); + + Type cachedType = typeArgs.get(t); + + logger.info("cached: {}", cachedType); + sb = new StringBuilder(); + } + + logger.info("generic return type: {}\n return type: {}", m.getGenericReturnType(), m.getReturnType()); + + logger.info("result:\n{}", sb); + } + } + + logger.info("methods: {} ", methods); + } } \ No newline at end of file diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/Tests.java b/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/Tests.java index f6db3a2..57c5017 100644 --- a/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/Tests.java +++ b/proteus-core/src/test/java/io/sinistral/proteus/test/controllers/Tests.java @@ -9,6 +9,7 @@ import java.io.File; import java.math.BigDecimal; import java.nio.ByteBuffer; +import java.nio.file.Paths; import java.sql.Timestamp; import java.time.Instant; import java.time.OffsetDateTime; @@ -53,6 +54,9 @@ import io.sinistral.proteus.test.wrappers.TestClassWrapper; import io.sinistral.proteus.test.wrappers.TestWrapper; import io.undertow.server.HttpServerExchange; +import io.undertow.server.handlers.form.FormData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author jbauer @@ -68,6 +72,7 @@ @Chain({TestClassWrapper.class}) public class Tests { + private static final Logger logger = LoggerFactory.getLogger(Tests.class.getName()); private static final ByteBuffer buffer; static { @@ -193,11 +198,33 @@ public ServerResponse> futureMap( ServerRequest request ) } @POST + @Debug @Path("response/file/path") @Produces(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.MULTIPART_FORM_DATA) public ServerResponse responseUploadFilePath(ServerRequest request, @FormParam("file") java.nio.file.Path file ) throws Exception - { + { + + FormData.FormValue formValue = request.files("file").getFirst(); + + logger.info("path: {} file: {} files: {}",file,file.toFile(), formValue.getFileName()); + + + File f = file.toFile(); + + File f2 = Paths.get(System.getProperty("user.home")+"/Desktop/tmp.mp4").toFile(); + + if(!f2.exists()) + { + f2.createNewFile(); + } + + logger.info("responseUploadFilePath: {} {} {}",f.getAbsolutePath(), f.length(), f.getName()); + + Files.copy(f, f2); + + + return response(ByteBuffer.wrap(Files.toByteArray(file.toFile()))).applicationOctetStream(); } @@ -455,6 +482,8 @@ public ServerResponse> uploadMultiplePathList(ServerRequest r public ServerResponse> uploadMultipleFileMap(ServerRequest request, @FormParam("files") Map files ) throws Exception { + + Map map = new HashMap<>(); for(String k : files.keySet()) diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/server/AbstractEndpointTest.java b/proteus-core/src/test/java/io/sinistral/proteus/test/server/AbstractEndpointTest.java index b23eb40..c47c266 100644 --- a/proteus-core/src/test/java/io/sinistral/proteus/test/server/AbstractEndpointTest.java +++ b/proteus-core/src/test/java/io/sinistral/proteus/test/server/AbstractEndpointTest.java @@ -1,4 +1,108 @@ package io.sinistral.proteus.test.server; +import io.restassured.RestAssured; +import org.apache.commons.io.FileUtils; +import org.junit.Before; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.runner.RunWith; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.stream.LongStream; + +@RunWith(DefaultServer.class) public class AbstractEndpointTest { + + public static File file = null; + + public static List files = new ArrayList<>(); + + public static Set idSet = new HashSet<>(); + + public static String PREFIX = "io.sinistral.proteus/test"; + + public static Path tmpPath = null; + + public static void initData() + { + try + { + tmpPath = Paths.get(System.getProperty("java.io.tmpdir")).resolve(PREFIX); + + if (!tmpPath.toFile().exists()) + { + tmpPath.toFile().mkdirs(); + } + + for (int i = 0; i < 4; i++) + { + byte[] bytes = new byte[1388608]; + Random random = new Random(); + random.nextBytes(bytes); + + Path dataPath = tmpPath.resolve("test-asset-" + i + ".mp4"); + + dataPath.toFile().deleteOnExit(); + + Files.write(dataPath, bytes); + + files.add(dataPath.toFile()); + + if(i == 0) + { + file = files.get(0); + } + + LongStream.range(1L, 10L).forEach(l -> { + + idSet.add(l); + }); + } + + } catch (Exception e) + { + e.printStackTrace(); + } + } + @BeforeAll + public static void init() + { + + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + + + } + + @AfterAll + public static void cleanup() + { + + try + { + FileUtils.deleteDirectory(tmpPath.toFile()); + } catch (Exception e) + { + e.printStackTrace(); + } + } + + @Before + public void setUp() + { + if(files.isEmpty()) + { + initData(); + } + + file = files.get(0); + } + } diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java b/proteus-core/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java index 2dcc27b..8e53e14 100644 --- a/proteus-core/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java +++ b/proteus-core/src/test/java/io/sinistral/proteus/test/server/DefaultServer.java @@ -5,6 +5,7 @@ import java.util.List; +import io.restassured.parsing.Parser; import io.sinistral.proteus.test.controllers.Tests; import org.junit.runner.Description; import org.junit.runner.Result; @@ -27,6 +28,7 @@ public class DefaultServer extends BlockJUnit4ClassRunner private static Logger log = LoggerFactory.getLogger(DefaultServer.class.getCanonicalName()); static { +RestAssured.defaultParser = Parser.JSON; System.setProperty("logback.configurationFile", "./conf/logback-test.xml"); } @@ -88,7 +90,7 @@ private static void runInternal(final RunNotifier notifier) { Thread.sleep(5000); - System.out.println(app.getPorts()); + List ports = app.getPorts(); diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/server/IsolatedEndpointsTest.java b/proteus-core/src/test/java/io/sinistral/proteus/test/server/IsolatedEndpointsTest.java index 8abe336..0b7968f 100644 --- a/proteus-core/src/test/java/io/sinistral/proteus/test/server/IsolatedEndpointsTest.java +++ b/proteus-core/src/test/java/io/sinistral/proteus/test/server/IsolatedEndpointsTest.java @@ -14,6 +14,7 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.junit.jupiter.api.TestInstance; import org.junit.runner.RunWith; import java.io.File; @@ -43,51 +44,9 @@ */ @RunWith(DefaultServer.class) @Ignore -public class IsolatedEndpointsTest { +@TestInstance(TestInstance.Lifecycle.PER_CLASS) - private File file = null; - - private List files = new ArrayList<>(); - - private Set idSet = new HashSet<>(); - - @Before - public void setUp() - { - - RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); - - try - { - for (int i = 0; i < 4; i++) - { - byte[] bytes = new byte[1388608]; - Random random = new Random(); - random.nextBytes(bytes); - - Path tmpPath = Files.createTempFile("test-asset", ".mp4"); - - tmpPath.toFile().deleteOnExit(); - - Files.write(tmpPath, bytes); - - files.add(tmpPath.toFile()); - - LongStream.range(1L, 10L).forEach(l -> { - - idSet.add(l); - }); - } - - file = files.get(0); - - } catch (Exception e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - fail(e.getMessage()); - } - } +public class IsolatedEndpointsTest extends AbstractEndpointTest{ @SuppressWarnings("resource") diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/server/StandardEndpointsTest.java b/proteus-core/src/test/java/io/sinistral/proteus/test/server/StandardEndpointsTest.java index 999ca1d..397c17c 100644 --- a/proteus-core/src/test/java/io/sinistral/proteus/test/server/StandardEndpointsTest.java +++ b/proteus-core/src/test/java/io/sinistral/proteus/test/server/StandardEndpointsTest.java @@ -17,6 +17,7 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.junit.jupiter.api.TestInstance; import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; @@ -50,56 +51,10 @@ /** * @author jbauer */ -@RunWith(DefaultServer.class) -public class StandardEndpointsTest { - - private File file = null; - - private List files = new ArrayList<>(); - - private Set idSet = new HashSet<>(); - - @Before - public void setUp() - { - - RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); - - - try - { - for (int i = 0; i < 4; i++) - { - byte[] bytes = new byte[1388608]; - Random random = new Random(); - random.nextBytes(bytes); - - Path tmpPath = Files.createTempFile("test-asset", ".mp4"); - - tmpPath.toFile().deleteOnExit(); - - Files.write(tmpPath, bytes); - - files.add(tmpPath.toFile()); - - LongStream.range(1L, 10L).forEach(l -> { - - idSet.add(l); - }); - } - - file = files.get(0); - - } catch (Exception e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - fail(e.getMessage()); - } - } - - @Test +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class StandardEndpointsTest extends AbstractEndpointTest { +@Test public void testDebugEndpoint() { @@ -696,28 +651,4 @@ public void responseComplexParameters() assertThat((map.get("optionalQueryDate").toString()), containsString("1970-01-01")); } - - - - // @Path("multipart/json") - - @After - public void tearDown() - { - - try - { - if (file.exists()) - { - file.delete(); - } - - } catch (Exception e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - fail(e.getMessage()); - } - } - } diff --git a/proteus-core/src/test/java/io/sinistral/proteus/test/server/UploadEndpointsTest.java b/proteus-core/src/test/java/io/sinistral/proteus/test/server/UploadEndpointsTest.java index 7803e96..81dc65d 100644 --- a/proteus-core/src/test/java/io/sinistral/proteus/test/server/UploadEndpointsTest.java +++ b/proteus-core/src/test/java/io/sinistral/proteus/test/server/UploadEndpointsTest.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.restassured.RestAssured; import io.restassured.http.ContentType; +import io.restassured.response.Response; import io.sinistral.proteus.protocol.MediaType; import io.sinistral.proteus.test.models.User; import io.sinistral.proteus.test.models.User.UserType; @@ -15,7 +16,10 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; +import org.junit.jupiter.api.TestInstance; import org.junit.runner.RunWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.ByteArrayOutputStream; import java.io.File; @@ -33,8 +37,7 @@ import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; /* * import static io.restassured.RestAssured.*; import static io.restassured.matcher.RestAssuredMatchers.*; import static org.hamcrest.Matchers.*; @@ -44,51 +47,18 @@ * @author jbauer */ @RunWith(DefaultServer.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class UploadEndpointsTest { +public class UploadEndpointsTest extends AbstractEndpointTest{ - private File file = null; + private static final Logger log = LoggerFactory.getLogger(UploadEndpointsTest.class.getName()); - private List files = new ArrayList<>(); - private Set idSet = new HashSet<>(); - - @Before - public void setUp() + @Test + public void testDebugEndpoint() { - RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); - - try - { - for (int i = 0; i < 4; i++) - { - byte[] bytes = new byte[1388608]; - Random random = new Random(); - random.nextBytes(bytes); - - Path tmpPath = Files.createTempFile("test-asset", ".mp4"); - - tmpPath.toFile().deleteOnExit(); - - Files.write(tmpPath, bytes); - - files.add(tmpPath.toFile()); - - LongStream.range(1L, 10L).forEach(l -> { - - idSet.add(l); - }); - } - - file = files.get(0); - - } catch (Exception e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - fail(e.getMessage()); - } + given().accept(ContentType.JSON).when().log().all().get("v1/tests/response/debug").then().statusCode(200).body(containsString("testValue")); } @Test @@ -98,7 +68,12 @@ public void responseUploadFilePathParameter() try { - final InputStream is = given().multiPart("file", file).contentType(MediaType.MULTIPART_FORM_DATA.contentType()).accept(ContentType.ANY).when().post("v1/tests/response/file/path").asInputStream(); + log.info("file: {}",file); + + final InputStream is = given().log().all() + .multiPart("file", file,MediaType.AUDIO_MP4.contentType()) + .accept(ContentType.JSON).when().post("v1/tests/response/file/path") + .then().extract().asInputStream(); try (final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { @@ -179,13 +154,21 @@ public void uploadMultipleFileMap() try { - Map map = given().multiPart("files", files.get(0)).multiPart("files", files.get(1)).multiPart("files", files.get(2)).multiPart("files", files.get(3)) - .contentType(MediaType.MULTIPART_FORM_DATA.contentType()) - .accept(ContentType.JSON).when().post("v1/tests/map/file").as(Map.class); + - assertThat(map.size(), equalTo(4)); + Response mapResponse = given().multiPart("files", files.get(0)).multiPart("files", files.get(1)).multiPart("files", files.get(2)).multiPart("files", files.get(3)) + .contentType(MediaType.MULTIPART_FORM_DATA.contentType()) + .accept(ContentType.JSON).when().post("v1/tests/map/file"); - assertThat(map.get(files.get(0).getName()), equalTo(files.get(0).getTotalSpace() + "")); +// + + ObjectMapper mapper = new ObjectMapper(); + + JsonNode node = mapper.readTree(mapResponse.asByteArray()); + + assertThat(node.size(), equalTo(4)); + + assertEquals(node.get(files.get(0).getName()).asText(), files.get(0).getTotalSpace() + ""); } catch (Exception e) { @@ -699,6 +682,8 @@ public void uploadMultipartFutureJson() JsonNode responseNode = mapper.readTree(is); + + assertThat(responseNode.get("id").toString(), containsString("101")); } catch (Exception e) diff --git a/proteus-core/src/test/resources/logback-test.xml b/proteus-core/src/test/resources/logback-test.xml index 16539fa..e6e3070 100644 --- a/proteus-core/src/test/resources/logback-test.xml +++ b/proteus-core/src/test/resources/logback-test.xml @@ -5,8 +5,8 @@ true - %date{ISO8601} %highlight(%-5level) [%boldCyan(%logger)] [%boldYellow(%method %F) ] - %boldWhite(%message) %n %red(%ex) - + %date{ISO8601} [%thread] %highlight(%-5level) [%boldCyan(%logger{36})] [%boldYellow(%method %F) %line] - %boldWhite(%message) %n %red(%rEx) %nopex + @@ -17,12 +17,12 @@ - + - + @@ -45,7 +45,7 @@ - + diff --git a/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerModelResolver.java b/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerModelResolver.java index b5981c2..c741ded 100644 --- a/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerModelResolver.java +++ b/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/jaxrs2/ServerModelResolver.java @@ -150,7 +150,7 @@ protected List resolveRequiredProperties(Annotated a, Annotation[] annot @Override protected boolean shouldIgnoreClass(Type type) { - // System.out.println("should ignore " + type); + // JavaType classType = TypeFactory.defaultInstance().constructType(type); String canonicalName = classType.toCanonical(); diff --git a/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/wrappers/HeaderApiKeyWrapper.java b/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/wrappers/HeaderApiKeyWrapper.java index 637e1e6..569a650 100644 --- a/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/wrappers/HeaderApiKeyWrapper.java +++ b/proteus-openapi/src/main/java/io/sinistral/proteus/openapi/wrappers/HeaderApiKeyWrapper.java @@ -49,7 +49,7 @@ public HttpHandler wrap(HttpHandler handler) Optional keyValue = Optional.ofNullable(exchange.getRequestHeaders().getFirst(API_KEY_HEADER)); - if(!keyValue.isPresent() || !keyValue.get().equals(API_KEY)) + if(keyValue.isEmpty() || !keyValue.get().equals(API_KEY)) { logger.error("Missing security credentials");