diff --git a/README.md b/README.md
new file mode 100644
index 0000000..426ff74
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+# PROTEUS
+
+An extremely lightweight, flexible and fast [Swagger](http://swagger.io/) first REST API framework atop [Undertow](http://undertow.io).
+A great deal of inspiration came from working with the following excellent projects: [Play](http://playframework.com), [Jooby](http://jooby.org), and [light-4j](https://github.com/networknt/light-4j).
+
+### 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).
+
+### Built With
+ - [Undertow](http://undertow.io) (server)
+ - [Guice](https://github.com/google/guice) (di)
+ - [Java Runtime Compiler](https://github.com/OpenHFT/Java-Runtime-Compiler) (runtime generated class compilation)
+ - [javapoet](https://github.com/square/javapoet) (runtime class generation)
+ - [Jackson](https://github.com/FasterXML/jackson-dataformat-xml) (xml)
+ - [jsoniter](http://jsoniter.com/) (json)
+ - [Logback](https://logback.qos.ch/) (logging)
+ - [Typesafe Config](https://github.com/typesafehub/config) (config)
+ - [Swagger](http://swagger.io/) (annotations and swagger spec)
+ - [jax-rs](http://docs.oracle.com/javaee/6/api/javax/ws/rs/package-summary.html) (annotations only)
+
+### Dependencies
+* [JDK 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html)
+* [Maven 3](http://maven.apache.org/)
+
+### Setup
+ - We are very impressed by what Jooby has done with server configuration
+ - Parameters are all configured in the ```conf/application.conf``` file
+ - Proteus applications generally have a main method that creates an instance of ```io.sinistral.proteus.Application```
+ - The user adds ```Service``` and ```Module``` classes to the application instance via ```addService``` and ```addModule``` methods prior to calling ```start```
+
+### Getting Started
+ - COMING SOON
+
+### Running
+
+ mvn exec:exec
+
+
diff --git a/pom.xml b/pom.xml
index 63e77f9..8f71b83 100644
--- a/pom.xml
+++ b/pom.xml
@@ -282,11 +282,6 @@
-
- org.javassist
- javassist
- 3.22.0-CR1
-
io.swagger
@@ -323,11 +318,6 @@
org.fusesource.jansi
jansi
1.15
-
-
- com.graphql-java
- graphql-java
- 2.3.0
io.sinistral
diff --git a/src/main/java/io/sinistral/proteus/ProteusApplication.java b/src/main/java/io/sinistral/proteus/ProteusApplication.java
index 011cc05..1830c11 100644
--- a/src/main/java/io/sinistral/proteus/ProteusApplication.java
+++ b/src/main/java/io/sinistral/proteus/ProteusApplication.java
@@ -36,7 +36,7 @@
import io.sinistral.proteus.modules.ConfigModule;
import io.sinistral.proteus.server.endpoints.EndpointInfo;
-import io.sinistral.proteus.server.handlers.DefaultHttpHandler;
+import io.sinistral.proteus.server.handlers.ServerDefaultHttpHandler;
import io.sinistral.proteus.server.handlers.HandlerGenerator;
import io.sinistral.proteus.services.AssetsService;
import io.sinistral.proteus.services.SwaggerService;
@@ -116,8 +116,8 @@ public void start()
if( rootHandlerClass == null && rootHandler == null )
{
- log.warn("No root handler class or root HttpHandler was specified, using default DefaultHttpHandler.");
- rootHandlerClass = DefaultHttpHandler.class;
+ log.warn("No root handler class or root HttpHandler was specified, using default ServerDefaultHttpHandler.");
+ rootHandlerClass = ServerDefaultHttpHandler.class;
}
log.info("Starting services...");
@@ -350,7 +350,7 @@ public static void main(String[] args)
app.addService(AssetsService.class);
- app.setRootHandlerClass(DefaultHttpHandler.class);
+ app.setRootHandlerClass(ServerDefaultHttpHandler.class);
app.start();
diff --git a/src/main/java/io/sinistral/proteus/annotations/Chain.java b/src/main/java/io/sinistral/proteus/annotations/Chain.java
new file mode 100644
index 0000000..40b2f67
--- /dev/null
+++ b/src/main/java/io/sinistral/proteus/annotations/Chain.java
@@ -0,0 +1,26 @@
+/**
+ *
+ */
+package io.sinistral.proteus.annotations;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import io.undertow.server.HandlerWrapper;
+
+@Retention(RUNTIME)
+@Target({ TYPE, METHOD })
+/**
+ * @author jbauer
+ *
+ */
+public @interface Chain
+{
+ Class extends HandlerWrapper>[] value();
+}
+
+
\ No newline at end of file
diff --git a/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java b/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java
index 3246ce4..1761851 100644
--- a/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java
+++ b/src/main/java/io/sinistral/proteus/server/handlers/HandlerGenerator.java
@@ -54,6 +54,7 @@
import io.sinistral.proteus.server.ServerResponse;
import io.sinistral.proteus.server.endpoints.EndpointInfo;
import io.swagger.annotations.Api;
+import io.undertow.server.HandlerWrapper;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.RoutingHandler;
@@ -750,7 +751,7 @@ else if(t.equals(HttpServerExchange.class) || t.equals(ServerRequest.class))
endpointInfo.setControllerMethod( m.getName());
- String methodName = String.format("%c%s%sHandler", Character.toLowerCase(clazz.getSimpleName().charAt(0)), clazz.getSimpleName().substring(1, clazz.getSimpleName().length()), StringUtils.capitalize(m.getName()));
+ String handlerName = String.format("%c%s%sHandler", Character.toLowerCase(clazz.getSimpleName().charAt(0)), clazz.getSimpleName().substring(1, clazz.getSimpleName().length()), StringUtils.capitalize(m.getName()));
TypeSpec.Builder handlerClassBuilder = TypeSpec.anonymousClassBuilder("").addSuperinterface(httpHandlerClass);
@@ -1025,12 +1026,43 @@ else if(producesContentType.contains(MediaType.TEXT_HTML))
- FieldSpec handlerField = FieldSpec.builder(httpHandlerClass, methodName, Modifier.FINAL).initializer("$L", handlerClassBuilder.build()).build();
+ FieldSpec handlerField = FieldSpec.builder(httpHandlerClass, handlerName, Modifier.FINAL).initializer("$L", handlerClassBuilder.build()).build();
initBuilder.addCode("$L\n", handlerField.toString());
+
+ Optional wrapAnnotation = Optional.ofNullable(m.getAnnotation(io.sinistral.proteus.annotations.Chain.class));
+
+ if( wrapAnnotation.isPresent() )
+ {
+ io.sinistral.proteus.annotations.Chain w = wrapAnnotation.get();
+
+ Class extends HandlerWrapper> wrapperClasses[] = w.value();
+
+ initBuilder.addStatement("$T currentHandler = $L", HttpHandler.class, handlerName);
+
+
+ for( int i = 0; i < wrapperClasses.length; i++ )
+ {
+ Class extends HandlerWrapper> wrapperClass = wrapperClasses[i];
+
+ String wrapperName = generateFieldName(wrapperClass.getCanonicalName());
+
+ initBuilder.addStatement("$T $L = new $T()", wrapperClass,wrapperName, wrapperClass);
- initBuilder.addStatement("$L.add(io.undertow.util.Methods.$L,$S,$L)", "router", httpMethod, methodPath, methodName);
+ initBuilder.addStatement("currentHandler = $L.wrap($L)", wrapperName, "currentHandler");
+
+ }
+
+ initBuilder.addStatement("$L.add(io.undertow.util.Methods.$L,$S,$L)", "router", httpMethod, methodPath, "currentHandler");
+
+ }
+ else
+ {
+ initBuilder.addStatement("$L.add(io.undertow.util.Methods.$L,$S,$L)", "router", httpMethod, methodPath, handlerName);
+ }
+
+
initBuilder.addCode("$L", "\n");
registeredEndpoints.add(endpointInfo);
@@ -1315,11 +1347,34 @@ public static String typeLiteralNameForType(Type type)
erasedTypeName = erasedParts[0];
}
- typeName = String.format("%s%s", Character.toLowerCase(erasedTypeName.charAt(0)), erasedTypeName.substring(1, erasedTypeName.length()));
+ typeName = generateFieldName(erasedTypeName);
return typeName;
}
+
+ public static String generateFieldName(String name)
+ {
+ String[] parts = name.split("\\.");
+
+ StringBuilder sb = new StringBuilder();
+
+ for( int i = 0; i < parts.length; i++ )
+ {
+ String part = parts[i];
+
+ if(i == 0)
+ {
+ sb.append(String.format("%s%s", Character.toLowerCase(part.charAt(0)), part.substring(1, part.length())));
+ }
+ else
+ {
+ sb.append(String.format("%s%s", Character.toUpperCase(part.charAt(0)), part.substring(1, part.length())));
+ }
+ }
+
+ return sb.toString();
+ }
public static void generateTypeLiteral(MethodSpec.Builder builder, Type type, String name)
{
diff --git a/src/main/java/io/sinistral/proteus/server/handlers/DefaultHttpHandler.java b/src/main/java/io/sinistral/proteus/server/handlers/ServerDefaultHttpHandler.java
similarity index 94%
rename from src/main/java/io/sinistral/proteus/server/handlers/DefaultHttpHandler.java
rename to src/main/java/io/sinistral/proteus/server/handlers/ServerDefaultHttpHandler.java
index 90541d9..81bc3bd 100644
--- a/src/main/java/io/sinistral/proteus/server/handlers/DefaultHttpHandler.java
+++ b/src/main/java/io/sinistral/proteus/server/handlers/ServerDefaultHttpHandler.java
@@ -20,7 +20,7 @@
/**
* @author jbauer
*/
-public class DefaultHttpHandler implements HttpHandler
+public class ServerDefaultHttpHandler implements HttpHandler
{
@Inject(optional=true)
@@ -33,7 +33,7 @@ public class DefaultHttpHandler implements HttpHandler
@Inject
- public DefaultHttpHandler(Config config)
+ public ServerDefaultHttpHandler(Config config)
{
Config globalHeaders = config.getConfig("globalHeaders");