diff --git a/.gitignore b/.gitignore index 9eb4605..585248f 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ bin .cache-tests /proteus/ /pom.xml.versionsBackup +.factorypath diff --git a/conf/logback.xml b/conf/logback.xml index 1a61aa3..e5e2656 100644 --- a/conf/logback.xml +++ b/conf/logback.xml @@ -5,109 +5,54 @@ true - %date{ISO8601} %highlight(%-5level) [%boldCyan(%logger)] [%boldYellow(%method %F) ] - %boldWhite(%message) %n %red(%ex) + %date{ISO8601} %highlight(%-5level) [%boldCyan(%logger)] + [%boldYellow(%method %F) ] - %boldWhite(%message) %n %red(%ex) - - - - - + + - + - - io.sinistral.proteus.services - - - - - - + - - + - - - - - - - - - - - + + + - - - - - - - - + + - - + - + - - - - - + - - + + + - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java b/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java index 0da34d3..00e55e8 100644 --- a/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java +++ b/src/main/java/io/sinistral/proteus/modules/ApplicationModule.java @@ -14,7 +14,9 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.JacksonXmlModule; import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.module.afterburner.AfterburnerModule; import com.google.common.util.concurrent.Service; @@ -58,17 +60,39 @@ protected void configure() { this.binder().requestInjection(this); - + + this.bindMappers(); + RoutingHandler router = new RoutingHandler(); + try + { + String className = config.getString("application.defaultResponseListener"); + log.info("Installing DefaultResponseListener " + className); + Class clazz = (Class) Class.forName(className); + this.bind(DefaultResponseListener.class).to(clazz).in(Singleton.class); + } catch (Exception e) + { + this.binder().addError(e); + log.error(e.getMessage(), e); + } + try { String className = config.getString("application.fallbackHandler"); log.info("Installing FallbackListener " + className); + Class clazz = (Class) Class.forName(className); - router.setFallbackHandler(clazz.newInstance()); + + HttpHandler fallbackHandler = clazz.newInstance(); + + this.binder().requestInjection(fallbackHandler); + + router.setFallbackHandler(fallbackHandler); + } catch (Exception e) { + this.binder().addError(e); log.error(e.getMessage(), e); } @@ -76,16 +100,7 @@ protected void configure() this.bind(ApplicationModule.class).toInstance(this); - try - { - String className = config.getString("application.defaultResponseListener"); - log.info("Installing DefaultResponseListener " + className); - Class clazz = (Class) Class.forName(className); - this.bind(DefaultResponseListener.class).to(clazz).in(Singleton.class); - } catch (Exception e) - { - log.error(e.getMessage(), e); - } + this.bind(new TypeLiteral>>() { @@ -103,8 +118,7 @@ protected void configure() { }).annotatedWith(Names.named("registeredHandlerWrappers")).toInstance(registeredHandlerWrappers); - - this.bindMappers(); + } @@ -113,12 +127,15 @@ protected void configure() */ public void bindMappers() { - this.bind(XmlMapper.class).toInstance(new XmlMapper()); - -// JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); -// JsonStream.setMode(EncodingMode.DYNAMIC_MODE); -// JsoniterAnnotationSupport.enable(); + JacksonXmlModule xmlModule = new JacksonXmlModule(); + xmlModule.setDefaultUseWrapper(false); + + XmlMapper xmlMapper = new XmlMapper(xmlModule); + xmlMapper.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION); + + this.bind(XmlMapper.class).toInstance(xmlMapper); + ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true); diff --git a/src/main/java/io/sinistral/proteus/server/handlers/ServerDefaultResponseListener.java b/src/main/java/io/sinistral/proteus/server/handlers/ServerDefaultResponseListener.java index febce1a..00fc792 100644 --- a/src/main/java/io/sinistral/proteus/server/handlers/ServerDefaultResponseListener.java +++ b/src/main/java/io/sinistral/proteus/server/handlers/ServerDefaultResponseListener.java @@ -51,22 +51,32 @@ public boolean handleDefaultResponse(HttpServerExchange exchange) return false; } - if (exchange.getStatusCode() >= 400) { + final int statusCode = exchange.getStatusCode(); + + if (statusCode >= 400) { + final Map errorMap = new HashMap<>(); + + final String path = exchange.getRelativePath(); + Throwable throwable = exchange.getAttachment(DefaultResponseListener.EXCEPTION); if( throwable == null ) { - throwable = new Exception("An unknown error occured"); + final String reason = StatusCodes.getReason(statusCode); + + throwable = new Exception(reason); } - - Map errorMap = new HashMap<>(); - + errorMap.put("exceptionClass", throwable.getClass().getName()); errorMap.put("message", throwable.getMessage()); - log.error(throwable.getMessage(),throwable); + errorMap.put("path", path); + + errorMap.put("code", Integer.toString(statusCode)); + + log.error(throwable.getMessage() + " at " + path,throwable); if( throwable.getStackTrace() != null ) { diff --git a/src/main/java/io/sinistral/proteus/server/handlers/ServerFallbackHandler.java b/src/main/java/io/sinistral/proteus/server/handlers/ServerFallbackHandler.java index b98df25..1b317ad 100644 --- a/src/main/java/io/sinistral/proteus/server/handlers/ServerFallbackHandler.java +++ b/src/main/java/io/sinistral/proteus/server/handlers/ServerFallbackHandler.java @@ -3,23 +3,82 @@ */ package io.sinistral.proteus.server.handlers; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.google.common.collect.ImmutableMap; +import com.google.inject.Inject; + +import io.sinistral.proteus.server.predicates.ServerPredicates; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.util.Headers; +import io.undertow.util.StatusCodes; /** * @author jbauer - * */ public class ServerFallbackHandler implements HttpHandler -{ - - @Override - public void handleRequest(HttpServerExchange exchange) throws Exception +{ + private class Message { + + @SuppressWarnings("unused") + public final Integer statusCode; + @SuppressWarnings("unused") + public final String reason; + + /** + * @param statusCode + * @param reason + */ + public Message(Integer statusCode, String reason) + { + this.statusCode = statusCode; + this.reason = reason; + } + } + + @Inject + protected XmlMapper xmlMapper; + + @Inject + protected ObjectMapper objectMapper; + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception + { + final int statusCode = 404; + + exchange.setStatusCode(statusCode); + + final String responseBody; + + final String reason = StatusCodes.getReason(statusCode); + + if (ServerPredicates.ACCEPT_JSON_PREDICATE.resolve(exchange)) + { + responseBody = objectMapper.writeValueAsString(new Message(statusCode,reason)); + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, javax.ws.rs.core.MediaType.APPLICATION_JSON); + } + else if (ServerPredicates.ACCEPT_XML_PREDICATE.resolve(exchange)) + { + responseBody = xmlMapper.writeValueAsString(new Message(statusCode,reason)); + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, javax.ws.rs.core.MediaType.APPLICATION_XML); + } + else if (ServerPredicates.ACCEPT_HTML_PREDICATE.resolve(exchange)) + { + responseBody = "Error" + statusCode + " - " + reason + ""; + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, javax.ws.rs.core.MediaType.TEXT_HTML); + } + else { - exchange.setStatusCode(404); - exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/plain"); - exchange.getResponseSender().send("Page Not Found!!"); - - } + responseBody = statusCode + " - " + reason; + exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, javax.ws.rs.core.MediaType.TEXT_PLAIN); + } + + exchange.getResponseHeaders().put(Headers.CONTENT_LENGTH, "" + responseBody.length()); + exchange.getResponseSender().send(responseBody); + + + + } } diff --git a/src/main/java/io/sinistral/proteus/server/predicates/ServerPredicates.java b/src/main/java/io/sinistral/proteus/server/predicates/ServerPredicates.java index c559274..ff08211 100644 --- a/src/main/java/io/sinistral/proteus/server/predicates/ServerPredicates.java +++ b/src/main/java/io/sinistral/proteus/server/predicates/ServerPredicates.java @@ -24,21 +24,19 @@ public class ServerPredicates public static final String XML_REGEX = "^(application\\/(xml|xhtml\\+xml)|text\\/xml)(;.*)?$"; + public static final String HTML_REGEX = "^(text\\/html)(;.*)?$"; + public static final String TEXT_REGEX = "^(text\\/plain)(;.*)?$"; public static final Predicate JSON_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.CONTENT_TYPE), JSON_REGEX); public static final Predicate XML_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.CONTENT_TYPE), XML_REGEX); - - //public static final Predicate JSON_PREDICATE = Predicates.contains(ExchangeAttributes.requestHeader(Headers.CONTENT_TYPE), MediaType.APPLICATION_JSON.contentType(), MediaType.APPLICATION_JSON.withCharset("utf-8")); - //public static final Predicate XML_PREDICATE = Predicates.contains(ExchangeAttributes.requestHeader(Headers.CONTENT_TYPE), MediaType.APPLICATION_XML.contentType(),MediaType.APPLICATION_XML.withCharset("UTF-8")); - + public static final Predicate HTML_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.CONTENT_TYPE), HTML_REGEX); public static final Predicate WILDCARD_PREDICATE = Predicates.contains(ExchangeAttributes.requestHeader(Headers.ACCEPT), MediaType.ANY.contentType()); public static final Predicate NO_WILDCARD_PREDICATE = Predicates.not(Predicates.contains(ExchangeAttributes.requestHeader(Headers.ACCEPT), MediaType.ANY.contentType())); - //public static final Predicate ACCEPT_JSON_PREDICATE = Predicates.contains(ExchangeAttributes.requestHeader(Headers.ACCEPT), MediaType.APPLICATION_JSON.contentType(),MediaType.APPLICATION_JSON.withCharset("UTF-8").toString()); public static final Predicate ACCEPT_JSON_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.ACCEPT), JSON_REGEX); - public static final Predicate ACCEPT_XML_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.ACCEPT), XML_REGEX);; - public static final Predicate ACCEPT_TEXT_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.ACCEPT), TEXT_REGEX);; -//public static final Predicate ACCEPT_XML_PREDICATE = Predicates.contains(ExchangeAttributes.requestHeader(Headers.ACCEPT), MediaType.APPLICATION_XML.contentType(),MediaType.APPLICATION_XML.withCharset("UTF-8").toString()); + public static final Predicate ACCEPT_XML_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.ACCEPT), XML_REGEX); + public static final Predicate ACCEPT_HTML_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.ACCEPT), HTML_REGEX); + public static final Predicate ACCEPT_TEXT_PREDICATE = Predicates.regex(ExchangeAttributes.requestHeader(Headers.ACCEPT), TEXT_REGEX); public static final Predicate ACCEPT_XML_EXCLUSIVE_PREDICATE = Predicates.and(ACCEPT_XML_PREDICATE, NO_WILDCARD_PREDICATE ); public static final Predicate MAX_CONTENT_SIZE_PREDICATE = new MaxRequestContentLengthPredicate.Builder().build(Collections.singletonMap("value", 0L)); public static final Predicate STRING_BODY_PREDICATE = Predicates.and(Predicates.or(JSON_PREDICATE,XML_PREDICATE), MAX_CONTENT_SIZE_PREDICATE ); diff --git a/src/main/resources/io/sinistral/proteus/swagger/index.html b/src/main/resources/io/sinistral/proteus/swagger/index.html index f24cc4f..7e96476 100644 --- a/src/main/resources/io/sinistral/proteus/swagger/index.html +++ b/src/main/resources/io/sinistral/proteus/swagger/index.html @@ -7,7 +7,6 @@ {{ title }} -