Skip to content

Commit

Permalink
Improve handler wrappers.
Browse files Browse the repository at this point in the history
  • Loading branch information
noboomu committed Sep 10, 2018
1 parent f70c107 commit 1d2674c
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 71 deletions.
78 changes: 40 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@

![Alt logo](https://cdn.rawgit.com/noboomu/proteus/master/src/main/resources/io/sinistral/proteus/proteus-logo.svg)

* An extremely lightweight, flexible, and fast [Swagger](http://swagger.io/) first MVC REST framework atop [Undertow](http://undertow.io).
* Inspired by: [Play](http://playframework.com), [Jooby](http://jooby.org), and [light-4j](https://github.com/networknt/light-4j).
* Verifiably [FAST AF](https://www.techempower.com/benchmarks/).
* [Latest benchmarks](https://www.techempower.com/benchmarks/) show Proteus at least 6x faster than Spring and Play across the board.
An extremely lightweight, flexible, and high performance [Undertow](http://undertow.io) based Java framework for developing and running microservices.

## Motivation
Verifiably [FAST](https://www.techempower.com/benchmarks/): [The latest benchmarks](https://www.techempower.com/benchmarks/) show Proteus ranks faster than 99% of other Java frameworks.

Inspired by [Play](http://playframework.com), [Jooby](http://jooby.org), and [light-4j](https://github.com/networknt/light-4j).



## Getting Started
COMING SOON

* Several years of working with the [Play](http://playframework.com) framework convinced us there had to be a better way.
* We faced a Goldilocks Crisis with the existing alternatives: [Jooby](http://jooby.org) did too much, [light-4j](https://github.com/networknt/light-4j) didn't do quite enough.
* We needed a framework that enabled us to write clean MVC REST controllers that created Swagger docs we could plug directly into the existing [codegen](https://github.com/swagger-api/swagger-codegen) solutions.
* We needed a framework with minimal overhead and performance at or near that of raw [Undertow](http://undertow.io).

## Under the Hood

Proteus takes your MVC controller classes and methods decorated with Swagger / JAX-RS annotations and generates native Undertow handler classes at runtime.

You can review the generated code by setting the ```io.sinistral.proteus.server``` log level to `DEBUG`.

## Setup

By default, the configuration is loaded into a `com.typesafe.config.Config` from a file at `conf/application.conf`.

Expand All @@ -38,7 +35,7 @@ Out of the box you get a [Swagger UI](https://github.com/swagger-api/swagger-ui)
> A `Module` implements `com.google.inject.Module`.
##### Example Application Class
#### Example Application Class

```java
public class ExampleApplication extends ProteusApplication
Expand All @@ -53,7 +50,7 @@ public class ExampleApplication extends ProteusApplication
}
}
```
##### Example Controller Class
#### Example Controller Class
```java
import java.nio.ByteBuffer;
import javax.ws.rs.*;
Expand Down Expand Up @@ -104,8 +101,8 @@ public class Examples

# Controllers

### Controller Class Annotations
---
### Supported Controller Annotations

Controller classes respect standard Swagger / JAX-RS annotations:
```java
@Api(tags="benchmarks")
Expand All @@ -114,9 +111,9 @@ Controller classes respect standard Swagger / JAX-RS annotations:
@Consumes((MediaType.MEDIA_TYPE_WILDCARD))
```

### Controller Method Annotations
---
Controller class methods also respect standard Swagger / JAX-RS annotations:
### Supported Method Annotations

Controller class methods respect standard Swagger / JAX-RS annotations:
```java
@GET
@Path("/plaintext")
Expand All @@ -129,17 +126,16 @@ public void plaintext(HttpServerExchange exchange)
```
In addition, the `io.sinistral.proteus.annotations.Blocking` annotation can be used to explicitly mark a method for blocked request handling.

### Return Types
---
##### HttpServerExchange = Total Control
## Return Types

##### Performance
For total control and maximum performance the raw `HttpServerExchange` can be passed to the controller method.

Methods that take an `HttpServerExchange` as an argument should __not__ return a value.

In this case the method takes on __full responsibility__ for completing the exchange.

##### ServerResponse = Convenience
##### Convenience
The static method ```io.sinistral.proteus.server.ServerResponse.response``` helps create ```ServerResponse<T>``` instances that are the preferred return type for controller methods.

If the response object's `contentType` is not explicitly set, the `@Produces` annotation is used in combination with the `Accept` headers to determine the `Content-Type`.
Expand Down Expand Up @@ -201,15 +197,15 @@ public void handleRequest(final io.undertow.server.HttpServerExchange exchange)
}
```

### Arguments
---
A ```io.sinistral.proteus.server.ServerRequest``` can be added as an endpoint argument if the user wishes to access request properties that are not included in the argument list.
## Controller Parameters

Proteus is capable of parsing most types of endpoint arguments automatically so long as the type has a ```fromString```, ```valueOf```, or can be deserialized from JSON.
A ```io.sinistral.proteus.server.ServerRequest``` can be added as an endpoint parameter if the user wishes to access request properties that are not included in the parameter list.

Proteus is capable of parsing most types of endpoint parameters automatically so long as the type has a ```fromString```, ```valueOf```, or can be deserialized from JSON.

Multipart/Form file uploads can be passed to the endpoint methods as a ```java.io.File```, a ```java.nio.Files.Path```, or a ```java.nio.ByteBuffer```.

Optional arguments are also supported, here is a more complex endpoint demonstrating several argument types:
Optional parameters are also supported, here is a more complex endpoint demonstrating several parameter types:
```java
@GET
@Path("/response/parameters/complex/{pathLong}")
Expand Down Expand Up @@ -248,11 +244,11 @@ public ServerResponse<Map<String,Object>> complexParameters(
return response(responseMap).applicationJson();
}
```
### Services
---
# Services

Proteus comes with two standard services that extend the ```io.sinistral.proteus.services.BaseService``` class.
#### AssetsService
---
## AssetsService

The AssetsService mounts an asset directory at a given path and is configured in your ```application.conf``` file.

The default configuration:
Expand All @@ -268,8 +264,8 @@ assets {
}
}
```
#### SwaggerService
---
## SwaggerService

The SwaggerService generates a swagger-spec file from your endpoints and serves a swagger-ui and spec.

The service is configured in your ```application.conf``` file.
Expand Down Expand Up @@ -302,17 +298,23 @@ swagger {
schemes = ["http"]
}
```
---
## Getting Started
COMING SOON


---

## Examples
COMING SOON

---


## Motivation

* Several years of working with the [Play](http://playframework.com) framework convinced us there had to be a better way.
* We faced a Goldilocks Crisis with the existing alternatives: [Jooby](http://jooby.org) did too much, [light-4j](https://github.com/networknt/light-4j) didn't do quite enough.
* We needed a framework that enabled us to write clean MVC REST controllers that created Swagger docs we could plug directly into the existing [codegen](https://github.com/swagger-api/swagger-codegen) solutions.
* We needed a framework with minimal overhead and performance at or near that of raw [Undertow](http://undertow.io).


### Dependencies

* [JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html)
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.sinistral</groupId>
<artifactId>proteus-core</artifactId>
<version>0.3.1-SNAPSHOT</version>
<version>0.3.2-SNAPSHOT</version>
<name>proteus core</name>
<description>Proteus is an extremely light, fast, and flexible Java REST API framework built atop Undertow.</description>
<url>http://github.com/noboomu/proteus</url>
Expand Down Expand Up @@ -34,7 +34,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version.maven-shade-plugin>3.0.0</version.maven-shade-plugin>
<version.undertow>1.4.13.Final</version.undertow>
<version.undertow>2.0.13.Final</version.undertow>
<version.jackson>2.9.6</version.jackson>
</properties>

Expand Down
23 changes: 4 additions & 19 deletions src/main/java/io/sinistral/proteus/ProteusApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -129,21 +129,6 @@ public void start()
log.info("Configuring modules: " + registeredModules);

Set<Module> modules = registeredModules.stream().map(mc -> injector.getInstance(mc)).collect(Collectors.toSet());

//boolean needsMappingModule = true;

// for(Module m : modules)
// {
// if(m.getClass().getSuperclass().equals(MappingModule.class))
// {
// needsMappingModule = false;
// }
// }
//
// if(needsMappingModule)
// {
// modules.add(injector.getInstance(MappingModule.class));
// }

injector = injector.createChildInjector(modules);

Expand Down Expand Up @@ -286,11 +271,11 @@ public void buildServer()
Undertow.Builder undertowBuilder = Undertow.builder().addHttpListener(httpPort, config.getString("application.host"))
.setBufferSize(16 * 1024)
.setIoThreads(Runtime.getRuntime().availableProcessors() * 2)
.setServerOption(UndertowOptions.ENABLE_HTTP2, config.getBoolean("undertow.enableHttp2"))
.setServerOption(UndertowOptions.ALWAYS_SET_DATE, true)
.setServerOption(UndertowOptions.ENABLE_HTTP2, config.getBoolean("undertow.server.enableHttp2"))
.setServerOption(UndertowOptions.ALWAYS_SET_DATE, config.getBoolean("undertow.server.alwaysSetDate"))
.setSocketOption(org.xnio.Options.BACKLOG, config.getInt("undertow.socket.backlog"))
.setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, false)
.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, false)
.setServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE, config.getBoolean("undertow.server.alwaysSetKeepAlive"))
.setServerOption(UndertowOptions.RECORD_REQUEST_START_TIME, config.getBoolean("undertow.server.recordRequestStartTime"))
.setServerOption(UndertowOptions.MAX_ENTITY_SIZE, config.getBytes("undertow.server.maxEntitySize"))
.setWorkerThreads(config.getInt("undertow.workerThreads"))
.setHandler(handler);
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/io/sinistral/proteus/server/Extractors.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public class Extractors
@Inject
public static ObjectMapper OBJECT_MAPPER;

public static Function<byte[],JsonNode> parseJson = (bytes) -> {
public static JsonNode parseJson(byte[] bytes) {
try
{
return OBJECT_MAPPER.readTree(bytes);
Expand All @@ -66,7 +66,7 @@ public static <T> java.util.Optional<T> extractWithFunction(final HttpServerExch

public static java.util.Optional<JsonNode> jsonNode(final HttpServerExchange exchange)
{
return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map(ByteBuffer::array).map(parseJson);
return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map(ByteBuffer::array).map( o -> parseJson(o));
}

public static <T> java.util.Optional<T> model(final HttpServerExchange exchange, final TypeReference<T> type )
Expand Down Expand Up @@ -175,7 +175,7 @@ public static java.util.Optional<ZonedDateTime> zonedDateTime(final HttpServerEx

public static java.util.Optional<JsonNode> any(final HttpServerExchange exchange )
{
return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map( b -> parseJson.apply(b.array()));
return java.util.Optional.ofNullable(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY)).map( b -> parseJson(b.array()));
}

public static java.util.Optional<Integer> integerValue(final HttpServerExchange exchange, final String name)
Expand Down Expand Up @@ -346,7 +346,7 @@ public static JsonNode any(final HttpServerExchange exchange )
{
try
{
return parseJson.apply( exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array() );
return parseJson( exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array() );
} catch (Exception e)
{
log.warn(e.getMessage(),e);
Expand All @@ -356,7 +356,7 @@ public static JsonNode any(final HttpServerExchange exchange )

public static JsonNode jsonNode(final HttpServerExchange exchange )
{
return parseJson.apply(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array());
return parseJson(exchange.getAttachment(ServerRequest.BYTE_BUFFER_KEY).array());
}

public static Path filePath(final HttpServerExchange exchange, final String name) throws java.lang.IllegalArgumentException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;



import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
Expand Down Expand Up @@ -195,6 +197,8 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class<?> cla
ClassName httpHandlerClass = ClassName.get("io.undertow.server", "HttpHandler");

String controllerName = clazz.getSimpleName().toLowerCase() + "Controller";

Integer handlerWrapperIndex = 1;

HashSet<String> handlerNameSet = new HashSet<>();

Expand Down Expand Up @@ -262,6 +266,13 @@ protected void addClassMethodHandlers(TypeSpec.Builder typeBuilder, Class<?> cla
{

}

/*
* if (t.getTypeName().matches("java\\.lang|java\\.nio|java\\.io|java\\.util"))
{
return false;
}
*/

if (t.getTypeName().contains("java.lang"))
{
Expand Down Expand Up @@ -500,7 +511,8 @@ else if (t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class))
}

log.debug("parameterizedLiteralsNameMap: " + parameterizedLiteralsNameMap);



Arrays.stream(m.getParameters()).forEachOrdered(p ->
{

Expand Down Expand Up @@ -585,6 +597,7 @@ else if (handler.equals(TypeHandler.FromStringType))
BeanParam beanParam = p.getAnnotation(BeanParam.class);

boolean isBeanParameter = beanParam != null;


TypeHandler t = TypeHandler.forType(type, isBeanParameter);

Expand Down Expand Up @@ -831,7 +844,7 @@ else if (producesContentType.contains(MediaType.TEXT_HTML))

if (wrapperName == null)
{
wrapperName = generateFieldName(wrapperClass.getCanonicalName());
wrapperName = String.format("%s_%d",generateFieldName(wrapperClass.getCanonicalName()),handlerWrapperIndex++) ;

initBuilder.addStatement("final $T $L = new $T()", wrapperClass, wrapperName, wrapperClass);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public String type()
@Override
public String format()
{
// TODO Auto-generated method stub

return null;
}

Expand Down

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,14 @@ swagger {
undertow
{
server {
enableHttp2 = false

alwaysSetDate = true
alwaysSetKeepAlive = false
recordRequestStartTime = false
maxEntitySize = 100M
maxEntitySize = 512M
bufferPipelinedData = false
enableHttp2=true

}

socket {
Expand All @@ -137,7 +139,6 @@ undertow
truststorePassword="password"
}

enableHttp2=false
# x AvailableProcessors
ioThreads = 16
workerThreads = 200
Expand Down

0 comments on commit 1d2674c

Please sign in to comment.