From a0c5cd596378cf8143d89cf50a1d53c97edd743b Mon Sep 17 00:00:00 2001 From: joshua bauer Date: Tue, 11 Apr 2017 07:42:29 -0700 Subject: [PATCH] Improve ServerResponse performance. --- src/main/java/com/wurrly/Application.java | 99 ++++++------ .../java/com/wurrly/controllers/Users.java | 36 +++-- .../java/com/wurrly/modules/ConfigModule.java | 31 +++- .../com/wurrly/modules/RoutingModule.java | 37 ++++- .../java/com/wurrly/server/Extractors.java | 9 +- .../com/wurrly/server/ServerResponse.java | 140 +++++++++++------ .../server/handlers/HandlerGenerator.java | 12 +- .../handlers/benchmark/BenchmarkHandlers.java | 145 +++++++++++++++++- .../ServerDefaultResponseListener.java | 52 +++++++ .../com/wurrly/server/swagger/Reader.java | 2 - .../com/wurrly/services/SwaggerService.java | 18 ++- 11 files changed, 436 insertions(+), 145 deletions(-) create mode 100644 src/main/java/com/wurrly/server/listeners/ServerDefaultResponseListener.java diff --git a/src/main/java/com/wurrly/Application.java b/src/main/java/com/wurrly/Application.java index 23ea46e..c210088 100644 --- a/src/main/java/com/wurrly/Application.java +++ b/src/main/java/com/wurrly/Application.java @@ -18,10 +18,9 @@ import com.google.common.util.concurrent.ServiceManager; import com.google.common.util.concurrent.ServiceManager.Listener; import com.google.inject.Guice; +import com.google.inject.Inject; import com.google.inject.Injector; -import com.j256.simplemagic.ContentType; -import com.jsoniter.any.Any; -import com.jsoniter.output.JsonStream; +import com.google.inject.name.Named; import com.typesafe.config.Config; import com.wurrly.controllers.Users; import com.wurrly.modules.ConfigModule; @@ -33,12 +32,12 @@ import io.undertow.Undertow; import io.undertow.UndertowOptions; +import io.undertow.server.DefaultResponseListener; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.server.RoutingHandler; import io.undertow.util.Headers; import io.undertow.util.HttpString; -import io.undertow.util.StatusCodes; /** * @author jbauer */ @@ -67,13 +66,25 @@ public static void notFoundHandler(HttpServerExchange exchange) { protected Injector injector = null; protected ServiceManager serviceManager = null; protected Undertow webServer = null; + protected Set> registeredServices = new HashSet<>(); + + @Inject + @Named("registeredControllers") + protected Set> registeredControllers; + + @Inject + @Named("registeredEndpoints") + protected Set registeredEndpoints; + protected RoutingModule routingModule = null; public Application() { - this.routingModule = new RoutingModule(); - injector = Guice.createInjector(new ConfigModule(),this.routingModule); + + injector = Guice.createInjector(new ConfigModule()); + injector.injectMembers(this); + } public void start() @@ -115,33 +126,33 @@ public void failure(Service service) public Undertow buildServer() { - Config rootConfig = injector.getInstance(Config.class); - RoutingHandler router = injector.getInstance(RoutingHandler.class); - RoutingModule routingModule = injector.getInstance(RoutingModule.class); + + final Config rootConfig = injector.getInstance(Config.class); + + final RoutingHandler router = injector.getInstance(RoutingHandler.class); - HandlerGenerator generator = new HandlerGenerator("com.wurrly.controllers.handlers","RouteHandlers",routingModule.getRegisteredControllers()); + final DefaultResponseListener defaultResponseListener = injector.getInstance(DefaultResponseListener.class); + + HandlerGenerator generator = new HandlerGenerator("com.wurrly.controllers.handlers","RouteHandlers",this.registeredControllers); injector.injectMembers(generator); Supplier generatedRouteSupplier = injector.getInstance(generator.compileClass()); router.addAll(generatedRouteSupplier.get()); - - Supplier benchmarkRouteSupplier = new BenchmarkHandlers(); - - router.addAll(benchmarkRouteSupplier.get()); + + router.addAll(new BenchmarkHandlers().get()); StringBuilder sb = new StringBuilder(); - Set routingInfo = routingModule.getRegisteredEndpoints(); //injector.getInstance(Key.get(new TypeLiteral>() {},Names.named("routeInfo"))); - - routingInfo.stream().forEachOrdered( r -> sb.append(r.toString()).append("\n")); + + this.registeredEndpoints.stream().forEachOrdered( r -> sb.append(r.toString()).append("\n")); log.info("\n\nRegistered the following endpoints: \n\n" + sb.toString()); webServer = Undertow.builder() .addHttpListener(rootConfig.getInt("application.port"), "localhost") - .setBufferSize(1024 * 16 * 10) + .setBufferSize(1024 * 16) .setIoThreads(Runtime.getRuntime().availableProcessors()) .setServerOption(UndertowOptions.ENABLE_HTTP2, true) .setServerOption(UndertowOptions.ALWAYS_SET_DATE, true) @@ -155,56 +166,42 @@ public Undertow buildServer() @Override public void handleRequest(final HttpServerExchange exchange) throws Exception { - try - { -// if(exchange.isInIoThread()) -// { -// exchange.dispatch(this); -// return; -// } + + exchange.addDefaultResponseListener(defaultResponseListener); - exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Origin"), "*"); - exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Methods"), "*"); - exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Headers"), "*"); +// exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Origin"), "*"); +// exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Methods"), "*"); +// exchange.getResponseHeaders().put(HttpString.tryFromString("Access-Control-Allow-Headers"), "*"); exchange.getResponseHeaders().put(Headers.SERVER, "Bowser"); - router.handleRequest(exchange); - - } - catch (IllegalArgumentException e) - { - if (exchange.isResponseChannelAvailable()) + try { + router.handleRequest(exchange); + } catch (Exception e) { - log.error(e.getMessage(),e); - - exchange.setStatusCode(StatusCodes.BAD_REQUEST); - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, ContentType.JSON.getMimeType()); - exchange.getResponseSender().send(JsonStream.serialize(Any.wrap(e))); - - } - } - catch (Exception e) - { - if (exchange.isResponseChannelAvailable()) - { - log.error(e.getMessage(),e); + if(exchange.isResponseChannelAvailable()) { + log.error(e.getMessage()); + exchange.endExchange(); + } } - } + } }).build(); return webServer; } - public void useService(Class serviceClass) + public Application useService(Class serviceClass) { this.registeredServices.add(serviceClass); + return this; } - public void useController(Class controllerClass) + public Application useController(Class controllerClass) { - this.routingModule.getRegisteredControllers().add(controllerClass); + this.registeredControllers.add(controllerClass); + return this; } + public static void main(String[] args) { diff --git a/src/main/java/com/wurrly/controllers/Users.java b/src/main/java/com/wurrly/controllers/Users.java index 077835a..b8840d7 100644 --- a/src/main/java/com/wurrly/controllers/Users.java +++ b/src/main/java/com/wurrly/controllers/Users.java @@ -31,12 +31,12 @@ import com.wurrly.models.User; import com.wurrly.server.ServerRequest; import com.wurrly.server.ServerResponse; +import static com.wurrly.server.ServerResponse.response; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; -import io.undertow.util.HttpString; -import io.undertow.util.StatusCodes; +import io.undertow.util.HttpString; /** * User API */ @@ -99,10 +99,10 @@ public ServerResponse userType( log.debug("optionalDate: " + optionalDate); - return ServerResponse.builder() + return response() .ok() - .withEntity(new User(232343L)) - .withHeader(HttpString.tryFromString("TestHeader"), "57475475") + .entity(new User(232343L)) + .header(HttpString.tryFromString("TestHeader"), "57475475") .build(); } @@ -111,7 +111,7 @@ public ServerResponse userType( @Path("/form/{userId}") @Consumes("*/*") @ApiOperation(value = "Post a complex form", httpMethod = "POST", response = User.class) - public Any userForm(@ApiParam(hidden=true) final ServerRequest serverRequest, + public ServerResponse userForm(@ApiParam(hidden=true) final ServerRequest serverRequest, @ApiParam(name="userId",required=true) @PathParam("userId") final Long userId, @ApiParam(name="context",required=false) @QueryParam("context") Optional context, @ApiParam(name="type",required=true) @QueryParam("type") User.UserType type, @@ -125,7 +125,7 @@ public Any userForm(@ApiParam(hidden=true) final ServerRequest serverRequest, log.debug("testFile: " + testFile); // // - return Any.wrap(new User(userId,type)); + return response().ok().entity(Any.wrap(new User(userId,type))).build(); } @@ -145,10 +145,10 @@ public ServerResponse user(@ApiParam(hidden=true)final ServerRequest serverReque // log.debug("context: " + context); // // - return ServerResponse.builder() + return response() .ok() - .jsonType() - .withBody(JsonStream.serialize(new User(userId))) + .applicationJson() + .body(JsonStream.serialize(new User(userId))) .build(); @@ -160,7 +160,7 @@ public ServerResponse user(@ApiParam(hidden=true)final ServerRequest serverReque //@Consumes("multipart/form-data") // @ApiImplicitParams({ @ApiImplicitParam(dataType = "com.wurrly.models.User", name = "user", paramType = "body", required = false, allowMultiple = false) }) @ApiOperation(value = "Create a user", httpMethod = "POST", response = User.class) - public Any createUser(@ApiParam(hidden=true)final ServerRequest serverRequest, @QueryParam("context") Optional context, final User user ) + public ServerResponse createUser(@ApiParam(hidden=true)final ServerRequest serverRequest, @QueryParam("context") Optional context, final User user ) { // @@ -168,7 +168,15 @@ public Any createUser(@ApiParam(hidden=true)final ServerRequest serverRequest, // log.debug("request: " + serverRequest); // log.debug("file: " + user); - return Any.wrap(new User(34L)); + + if( user != null ) + { + return response().ok().entity(user).build(); + } + else + { + return response().exception(new Exception("No user found")).build(); + } @@ -179,7 +187,7 @@ public Any createUser(@ApiParam(hidden=true)final ServerRequest serverRequest, @Consumes(("application/json")) // @ApiImplicitParams({ @ApiImplicitParam(dataType = "com.wurrly.models.User", name = "user", paramType = "body", required = false, allowMultiple = false) }) @ApiOperation(value = "Update a user's name", httpMethod = "PUT", response = User.class) - public Any updateUsername(@ApiParam(hidden=true)final ServerRequest serverRequest, @QueryParam("context") Optional context, final User user ) + public ServerResponse updateUsername(@ApiParam(hidden=true)final ServerRequest serverRequest, @QueryParam("context") Optional context, final User user ) { // log.debug("esIndexName: " + esIndexName); @@ -188,7 +196,7 @@ public Any updateUsername(@ApiParam(hidden=true)final ServerRequest serverReques log.debug("file: " + user); - return Any.wrap(user); + return response().entity(Any.wrap(user)).build(); } diff --git a/src/main/java/com/wurrly/modules/ConfigModule.java b/src/main/java/com/wurrly/modules/ConfigModule.java index d82a1a9..1bcacf3 100644 --- a/src/main/java/com/wurrly/modules/ConfigModule.java +++ b/src/main/java/com/wurrly/modules/ConfigModule.java @@ -18,6 +18,11 @@ import com.google.inject.name.Named; import com.google.inject.name.Names; import com.google.inject.util.Types; +import com.jsoniter.DecodingMode; +import com.jsoniter.JsonIterator; +import com.jsoniter.annotation.JsoniterAnnotationSupport; +import com.jsoniter.output.EncodingMode; +import com.jsoniter.output.JsonStream; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import com.typesafe.config.ConfigObject; @@ -37,7 +42,7 @@ public class ConfigModule extends AbstractModule */ protected String configFile = "application.conf"; - + protected Config config = null; public ConfigModule() { @@ -59,6 +64,25 @@ protected void configure() this.bindConfig(fileConfig(configFile)); } + JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + JsoniterAnnotationSupport.enable(); + + install(new RoutingModule(this.config)); + + + +// try +// { +// Class defaultResponseListener = (Class) Class.forName(config.getString("application.defaultResponseListener")); +// +// this.bind(DefaultResponseListener.class).to(defaultResponseListener).in(Singleton.class); +// +// } catch (Exception e) +// { +// log.error(e.getMessage(),e); +// } + } @@ -93,7 +117,10 @@ public void bindConfig(final Config config) } } // bind config - this.binder().bind(Config.class).toInstance( ConfigFactory.load(config) ); + + this.config = ConfigFactory.load(config); + + this.binder().bind(Config.class).toInstance( config ); log.info("Config:\n" + config); diff --git a/src/main/java/com/wurrly/modules/RoutingModule.java b/src/main/java/com/wurrly/modules/RoutingModule.java index f807885..951192a 100644 --- a/src/main/java/com/wurrly/modules/RoutingModule.java +++ b/src/main/java/com/wurrly/modules/RoutingModule.java @@ -6,19 +6,19 @@ import java.util.HashSet; import java.util.Set; import java.util.TreeSet; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.inject.AbstractModule; -import com.google.inject.Scope; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.name.Names; +import com.typesafe.config.Config; import com.wurrly.Application.BaseHandlers; import com.wurrly.server.endpoints.EndpointInfo; +import io.undertow.server.DefaultResponseListener; import io.undertow.server.RoutingHandler; /** @@ -32,18 +32,43 @@ public class RoutingModule extends AbstractModule protected Set registeredEndpoints = new TreeSet<>(); protected Set> registeredControllers = new HashSet<>(); - + + protected Config config; + + public RoutingModule(Config config) + { + this.config = config; + } + + + @SuppressWarnings("unchecked") @Override protected void configure() { - RoutingHandler router = new RoutingHandler().setFallbackHandler(BaseHandlers::notFoundHandler); + RoutingHandler router = new RoutingHandler() + .setFallbackHandler(BaseHandlers::notFoundHandler); + + this.binder().requestInjection(this); this.bind(RoutingHandler.class).toInstance(router); this.bind(RoutingModule.class).toInstance(this); - + + + try + { + String defaultResponseListenerClassName = config.getString("application.defaultResponseListener"); + Class defaultResponseListenerClass = (Class) Class.forName(defaultResponseListenerClassName); + log.info("defaultResponseListenerClassName: " + defaultResponseListenerClassName); + this.bind(DefaultResponseListener.class).to(defaultResponseListenerClass).in(Singleton.class); + } catch (Exception e) + { + log.error(e.getMessage(),e); + } + this.bind(new TypeLiteral>>() {}).annotatedWith(Names.named("registeredControllers")).toInstance(registeredControllers); - + this.bind(new TypeLiteral>() {}).annotatedWith(Names.named("registeredEndpoints")).toInstance(registeredEndpoints); + } /** diff --git a/src/main/java/com/wurrly/server/Extractors.java b/src/main/java/com/wurrly/server/Extractors.java index 3060b71..3df3990 100644 --- a/src/main/java/com/wurrly/server/Extractors.java +++ b/src/main/java/com/wurrly/server/Extractors.java @@ -150,7 +150,7 @@ public static Date date(final HttpServerExchange exchange,final String name) { } - public static T typed(final HttpServerExchange exchange, final TypeLiteral type ) + public static T typed(final HttpServerExchange exchange, final TypeLiteral type ) throws Exception { try { @@ -158,11 +158,11 @@ public static T typed(final HttpServerExchange exchange, final TypeLiteral< } catch( Exception e ) { - return null; + throw new IllegalArgumentException("Invalid JSON"); } } - public static T typed(final HttpServerExchange exchange, final Class type ) + public static T typed(final HttpServerExchange exchange, final Class type ) throws Exception { try { @@ -170,7 +170,8 @@ public static T typed(final HttpServerExchange exchange, final Class typ } catch( Exception e ) { - return null; + throw new IllegalArgumentException("Invalid JSON"); + } } diff --git a/src/main/java/com/wurrly/server/ServerResponse.java b/src/main/java/com/wurrly/server/ServerResponse.java index 16c7c78..adb83bd 100644 --- a/src/main/java/com/wurrly/server/ServerResponse.java +++ b/src/main/java/com/wurrly/server/ServerResponse.java @@ -11,6 +11,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.jsoniter.any.Any; import com.jsoniter.output.JsonStream; import io.undertow.io.IoCallback; @@ -30,15 +31,23 @@ public class ServerResponse { private static Logger log = LoggerFactory.getLogger(ServerResponse.class.getCanonicalName()); + private static String APPLICATION_JSON_CONTENT_TYPE = org.apache.http.entity.ContentType.APPLICATION_JSON.getMimeType(); + private static String TEXT_PLAIN_CONTENT_TYPE = org.apache.http.entity.ContentType.TEXT_PLAIN.getMimeType(); + private static String TEXT_XML_CONTENT_TYPE = org.apache.http.entity.ContentType.TEXT_XML.getMimeType(); + private static String TEXT_HTML_CONTENT_TYPE = org.apache.http.entity.ContentType.TEXT_HTML.getMimeType(); + private ByteBuffer body; - private int status = StatusCodes.ACCEPTED; + private int status = StatusCodes.OK; private final HeaderMap headers = new HeaderMap(); private final Map cookies = new HashMap<>(); - private String contentType = org.apache.http.entity.ContentType.TEXT_PLAIN.getMimeType(); + private String contentType = null; private Object entity; - private IoCallback callback; + private IoCallback ioCallback; + private boolean hasCookies = false; + private boolean hasHeaders = false; + private boolean hasIoCallback = false; public ServerResponse() { @@ -76,17 +85,17 @@ public String getContentType() /** * @return the callback */ - public IoCallback getCallback() + public IoCallback getIoCallback() { - return callback; + return ioCallback; } /** * @param callback the callback to set */ - public void setCallback(IoCallback callback) + public void setIoCallback(IoCallback ioCallback) { - this.callback = callback; + this.ioCallback = ioCallback; } /** @@ -115,36 +124,44 @@ public void setContentType(String contentType) public void send( final HttpHandler handler, final HttpServerExchange exchange ) { - long itr = this.headers.fastIterateNonEmpty(); - - while( itr != -1L ) + if( this.hasHeaders ) { - final HeaderValues values = this.headers.fiCurrent(itr); - - exchange.getResponseHeaders().putAll(values.getHeaderName(), values); + long itr = this.headers.fastIterateNonEmpty(); - itr = this.headers.fiNextNonEmpty(itr); + while( itr != -1L ) + { + final HeaderValues values = this.headers.fiCurrent(itr); + + exchange.getResponseHeaders().putAll(values.getHeaderName(), values); + + itr = this.headers.fiNextNonEmpty(itr); + } } - exchange.getResponseCookies().putAll(this.cookies); + if( this.hasCookies ) + { + exchange.getResponseCookies().putAll(this.cookies); + } exchange.setStatusCode( this.status ); - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, this.contentType); - - if( this.body == null && this.entity == null ) - { - exchange.endExchange(); + if( this.contentType != null ) + { + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, this.contentType); } - else if( this.body != null) + + + + if( this.body != null) { - if( this.callback != null ) + if( !this.hasIoCallback ) { - exchange.getResponseSender().send(this.body,this.callback); + exchange.getResponseSender().send(this.body); } else { - exchange.getResponseSender().send(this.body); + exchange.getResponseSender().send(this.body,this.ioCallback); + } } else if( this.entity != null) @@ -174,11 +191,14 @@ else if( this.entity != null) exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain"); exchange.getResponseSender().send(e.getMessage()); } - exchange.endExchange(); } + else + { + exchange.endExchange(); + } } @@ -186,7 +206,7 @@ else if( this.entity != null) * Creates builder to build {@link ServerResponse}. * @return created builder */ - public static Builder builder() + public static Builder response() { return new Builder(); } @@ -196,81 +216,97 @@ public static Builder builder() */ public static final class Builder { - private ServerResponse response = new ServerResponse(); + private final ServerResponse response; private Builder() { + this.response = new ServerResponse(); } - public Builder withBody(ByteBuffer body) + public Builder body(ByteBuffer body) { this.response.body = body; return this; } - public Builder withEntity(Object entity) + public Builder entity(Object entity) { this.response.entity = entity; - jsonType(); + applicationJson(); return this; } - public Builder withBody(String body) + public Builder body(String body) { this.response.body = ByteBuffer.wrap(body.getBytes()); return this; } - public Builder withStatus(int status) + public Builder status(int status) { this.response.status = status; return this; } - public Builder withHeader(HttpString headerName, String value) + public Builder header(HttpString headerName, String value) { this.response.headers.put(headerName, value); + this.response.hasHeaders = true; return this; } - public Builder withCookie(String cookieName, Cookie cookie) + public Builder cookie(String cookieName, Cookie cookie) { - this.response.getCookies().put(cookieName, cookie); + this.response.cookies.put(cookieName, cookie); + this.response.hasCookies = true; + return this; } - public Builder withContentType(String contentType) + public Builder contentType(String contentType) { this.response.contentType = contentType; return this; } - public Builder withCallback(IoCallback callback) + public Builder ioCallback(IoCallback ioCallback) + { + this.response.ioCallback = ioCallback; + return this; + } + + public Builder applicationJson() { - this.response.callback = callback; + this.response.contentType = APPLICATION_JSON_CONTENT_TYPE; return this; } - public Builder jsonType() + public Builder textHtml() { - this.response.contentType = org.apache.http.entity.ContentType.APPLICATION_JSON.getMimeType(); + this.response.contentType = TEXT_HTML_CONTENT_TYPE; return this; } - public Builder htmlType() + public Builder textXml() { - this.response.contentType = org.apache.http.entity.ContentType.TEXT_HTML.getMimeType(); + this.response.contentType = TEXT_XML_CONTENT_TYPE; return this; } - public Builder plainType() + public Builder textPlain() { - this.response.contentType = org.apache.http.entity.ContentType.TEXT_PLAIN.getMimeType(); + this.response.contentType = TEXT_PLAIN_CONTENT_TYPE; return this; } public Builder ok() + { + this.response.status = StatusCodes.OK; + return this; + } + + public Builder accepted() { this.response.status = StatusCodes.ACCEPTED; return this; @@ -318,6 +354,22 @@ public Builder noContent() this.response.status = StatusCodes.NO_CONTENT; return this; } + + public Builder withIoCallback(IoCallback ioCallback) + { + this.response.ioCallback = ioCallback; + this.response.hasIoCallback = ioCallback == null; + return this; + } + + public Builder exception(Throwable t) + { + if(this.response.status == StatusCodes.ACCEPTED) + { + badRequest(); + } + return this.entity(Any.wrap(t)); + } public ServerResponse build() { diff --git a/src/main/java/com/wurrly/server/handlers/HandlerGenerator.java b/src/main/java/com/wurrly/server/handlers/HandlerGenerator.java index 2f222ba..cd19827 100644 --- a/src/main/java/com/wurrly/server/handlers/HandlerGenerator.java +++ b/src/main/java/com/wurrly/server/handlers/HandlerGenerator.java @@ -230,7 +230,6 @@ public static void addStatement(MethodSpec.Builder builder, Parameter parameter) public static TypeHandler forType(Type type) { - boolean isEnum = false; boolean hasValueOf = false; boolean hasFromString = false; boolean isOptional = type.getTypeName().contains("java.util.Optional"); @@ -240,9 +239,7 @@ public static TypeHandler forType(Type type) { try { - Class clazz = Class.forName(type.getTypeName()); - - isEnum = clazz.isEnum(); + Class clazz = Class.forName(type.getTypeName()); hasValueOf = hasValueOfMethod(clazz); @@ -369,10 +366,7 @@ else if( hasFromStringMethod(erasedType) ) return OptionalStringType; } } - else if (isEnum) - { - return EnumType; - } + else if (hasValueOf) { return ValueOfType; @@ -390,6 +384,8 @@ else if (hasFromString) @Inject protected RoutingModule routingModule; + + @Inject @Named("application.path") diff --git a/src/main/java/com/wurrly/server/handlers/benchmark/BenchmarkHandlers.java b/src/main/java/com/wurrly/server/handlers/benchmark/BenchmarkHandlers.java index 7f4d359..093b3fa 100644 --- a/src/main/java/com/wurrly/server/handlers/benchmark/BenchmarkHandlers.java +++ b/src/main/java/com/wurrly/server/handlers/benchmark/BenchmarkHandlers.java @@ -4,10 +4,13 @@ package com.wurrly.server.handlers.benchmark; import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; import java.util.function.Supplier; import org.slf4j.Logger; @@ -16,6 +19,7 @@ import com.google.common.io.Files; import com.j256.simplemagic.ContentType; import com.jsoniter.output.JsonStream; +import com.wurrly.server.ServerResponse; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; @@ -24,6 +28,7 @@ import io.undertow.server.handlers.resource.ResourceHandler; import io.undertow.util.Headers; import io.undertow.util.Methods; +import io.undertow.util.StatusCodes; /** * @author jbauer @@ -34,6 +39,8 @@ public class BenchmarkHandlers implements Supplier private static Logger log = LoggerFactory.getLogger(BenchmarkHandlers.class.getCanonicalName()); + private final static String PLAIN_TEXT = "text/plain".intern(); + public final static class BenchmarkMessage { public String message = "hello world"; @@ -42,10 +49,10 @@ public final static class BenchmarkMessage public RoutingHandler get() { final ByteBuffer msgBuffer = ByteBuffer.wrap("hello world".getBytes()); + + final RoutingHandler handler = new RoutingHandler(); - RoutingHandler handler = new RoutingHandler(); - - handler.add(Methods.GET, "/string", new HttpHandler(){ + handler.add(Methods.GET, "/string".intern(), new HttpHandler(){ @Override public void handleRequest(HttpServerExchange exchange) throws Exception @@ -57,7 +64,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception } } ); - handler.add(Methods.GET, "/json", new HttpHandler(){ + handler.add(Methods.GET, "/json".intern(), new HttpHandler(){ @Override public void handleRequest(HttpServerExchange exchange) throws Exception @@ -69,6 +76,100 @@ public void handleRequest(HttpServerExchange exchange) throws Exception } } ); + handler.add(Methods.GET, "/string3".intern(), new HttpHandler(){ + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception + { + // TODO Auto-generated method stub + + exchange.setStatusCode( 200 ); + + //exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, PLAIN_TEXT); + + exchange.getResponseSender().send(msgBuffer); + + } + } ); + + handler.add(Methods.GET, "/string4".intern(), new HttpHandler(){ + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception + { + // TODO Auto-generated method stub + + exchange.getResponseSender().send(msgBuffer); + + } + } ); + + handler.add(Methods.GET, "/string2".intern(), new HttpHandler(){ + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception + { + // TODO Auto-generated method stub + + final ServerResponse resp = ServerResponse.response().body(msgBuffer).build(); + + resp.send(this, exchange); + + } + } ); + + + + handler.add(Methods.GET, "/mvc/json", new HttpHandler(){ + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception + { + // TODO Auto-generated method stub + + ServerResponse resp = ServerResponse.response().body(JsonStream.serialize(new BenchmarkMessage())).build(); + + resp.send(this, exchange); + + } + } ); + + handler.add(Methods.GET, "/async", new HttpHandler(){ + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception + { + // TODO Auto-generated method stub + + //exchange.getResponseSender().send(JsonStream.serialize(new BenchmarkMessage())); + + if (exchange.isInIoThread()) { + exchange.dispatch(this); + return; + } + + CompletionStage future = authenticate(); + + future.thenAccept( (passed) -> { + + exchange.setStatusCode(StatusCodes.ACCEPTED); + exchange.getResponseSender().send("User authenticated"); + + }); + + future.exceptionally( (throwable) -> { + + + exchange.setStatusCode(StatusCodes.FORBIDDEN); + exchange.getResponseSender().send(throwable.getMessage()); + + return null; + + }); + + } + } ); + handler.add(Methods.GET, "/video.mp4", new HttpHandler(){ @Override @@ -96,7 +197,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception } } ); - handler.add(Methods.GET, "/bytes.mp4", new HttpHandler(){ + handler.add(Methods.GET, "/bytes.mp4".intern(), new HttpHandler(){ @Override public void handleRequest(HttpServerExchange exchange) throws Exception @@ -192,10 +293,40 @@ public void handleRequest(HttpServerExchange exchange) throws Exception } } ); + + return handler; } + private static ExecutorService EXECUTOR = Executors.newCachedThreadPool(); + + public static CompletionStage authenticate() + { + CompletableFuture future = new CompletableFuture<>(); + + + int passed = (int)((Math.random() * 1000)); + + try + { + Thread.sleep(4000L); + + } catch (Exception e) + { + // TODO: handle exception + } + if(passed > 500) + { + future.complete(true); + } + else + { + future.completeExceptionally(new Exception("Failed to authenticate")); + } + + return future; + } } diff --git a/src/main/java/com/wurrly/server/listeners/ServerDefaultResponseListener.java b/src/main/java/com/wurrly/server/listeners/ServerDefaultResponseListener.java new file mode 100644 index 0000000..4bf1f1d --- /dev/null +++ b/src/main/java/com/wurrly/server/listeners/ServerDefaultResponseListener.java @@ -0,0 +1,52 @@ +/** + * + */ +package com.wurrly.server.listeners; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableMap; +import com.jsoniter.output.JsonStream; + +import io.undertow.server.DefaultResponseListener; +import io.undertow.server.HttpServerExchange; +import io.undertow.util.Headers; + +/** + * @author jbauer + * + */ +public class ServerDefaultResponseListener implements DefaultResponseListener +{ + + + @Override + public boolean handleDefaultResponse(HttpServerExchange exchange) + { + if (!exchange.isResponseChannelAvailable()) { + return false; + } + + if (exchange.getStatusCode() == 500) { + + + Throwable throwable = exchange.getAttachment(DefaultResponseListener.EXCEPTION); + + if( throwable == null ) + { + throwable = new Exception("An unknown error occured"); + } + + final String jsonBody = JsonStream.serialize(throwable); + + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, org.apache.http.entity.ContentType.APPLICATION_JSON.getMimeType()); + exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, jsonBody.length()); + exchange.getResponseSender().send(jsonBody); + + return true; + } + return false; + } + +} diff --git a/src/main/java/com/wurrly/server/swagger/Reader.java b/src/main/java/com/wurrly/server/swagger/Reader.java index 9f32c61..f847139 100644 --- a/src/main/java/com/wurrly/server/swagger/Reader.java +++ b/src/main/java/com/wurrly/server/swagger/Reader.java @@ -1095,8 +1095,6 @@ private List getParameters(Type type, List annotations, j if (ParameterProcessor.applyAnnotations(swagger, parameter, type, annotations) != null) { - LOGGER.info(parameter.getDescription() + " -> " + type); - processed.add(parameter); } } diff --git a/src/main/java/com/wurrly/services/SwaggerService.java b/src/main/java/com/wurrly/services/SwaggerService.java index a745158..ff90aac 100644 --- a/src/main/java/com/wurrly/services/SwaggerService.java +++ b/src/main/java/com/wurrly/services/SwaggerService.java @@ -101,8 +101,12 @@ public class SwaggerService extends ConfigurableService implements Supplier registeredEndpoints; + + @Inject + @Named("registeredControllers") + protected Set> registeredControllers; public SwaggerService() { @@ -112,7 +116,7 @@ public SwaggerService() public void generateSwaggerSpec() { - Set> classes = this.routingModule.getRegisteredControllers(); + Set> classes = this.registeredControllers; List extensions = new ArrayList<>(); @@ -248,7 +252,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception }); - routingModule.getRegisteredEndpoints().add(EndpointInfo.builder().withConsumes("*/*").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).withProduces(ContentType.JSON.getMimeType()).build()); + this.registeredEndpoints.add(EndpointInfo.builder().withConsumes("*/*").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).withProduces(ContentType.JSON.getMimeType()).build()); pathTemplate = this.swaggerBasePath; @@ -267,7 +271,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception }); - routingModule.getRegisteredEndpoints().add(EndpointInfo.builder().withConsumes("*/*").withProduces("text/html").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).build()); + this.registeredEndpoints.add(EndpointInfo.builder().withConsumes("*/*").withProduces("text/html").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).build()); ClassPathResourceManager resourceManager = new ClassPathResourceManager(this.getClass().getClassLoader()); @@ -292,7 +296,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception }); - routingModule.getRegisteredEndpoints().add(EndpointInfo.builder().withConsumes("*/*").withProduces("text/css").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).build()); + this.registeredEndpoints.add(EndpointInfo.builder().withConsumes("*/*").withProduces("text/css").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).build()); try @@ -321,7 +325,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception - routingModule.getRegisteredEndpoints().add(EndpointInfo.builder().withConsumes("*/*").withProduces("*/*").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).build()); + this.registeredEndpoints.add(EndpointInfo.builder().withConsumes("*/*").withProduces("*/*").withPathTemplate(pathTemplate).withControllerName("Swagger").withMethod(Methods.GET).build());