Skip to content

Commit

Permalink
ParametersValidator interface; validation-javax module & SimpleParame…
Browse files Browse the repository at this point in the history
…tersValidator with javax.validation
  • Loading branch information
Gmugra committed Dec 30, 2020
1 parent fa3fc47 commit bc214de
Show file tree
Hide file tree
Showing 27 changed files with 384 additions and 96 deletions.
20 changes: 9 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

Lightweight Java library for HTTP requests routing in context of Servlet API

[![Build Status](https://travis-ci.com/Gmugra/net.cactusthorn.routing.svg?branch=main)](https://travis-ci.com/Gmugra/net.cactusthorn.routing) [![Coverage Status](https://coveralls.io/repos/github/Gmugra/net.cactusthorn.routing/badge.svg?branch=main)](https://coveralls.io/github/Gmugra/net.cactusthorn.routing?branch=main) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Gmugra/net.cactusthorn.routing)
[![Build Status](https://travis-ci.com/Gmugra/net.cactusthorn.routing.svg?branch=main)](https://travis-ci.com/Gmugra/net.cactusthorn.routing) [![Coverage Status](https://coveralls.io/repos/github/Gmugra/net.cactusthorn.routing/badge.svg?branch=main)](https://coveralls.io/github/Gmugra/net.cactusthorn.routing?branch=main) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Gmugra/net.cactusthorn.routing) [![Build by Maven](http://maven.apache.org/images/logos/maven-feather.png)](http://maven.apache.org)

## Introduction

Expand Down Expand Up @@ -57,6 +57,7 @@ public class Application {
.addConsumer("application/json", new SimpleGsonConsumer())
.addProducer("text/html", new SimpleThymeleafProducer("/thymeleaf/"))
.addConverter(java.time.LocalDate.class, converter);
.setParametersValidator(new SimpleParametersValidator())
.build();

MultipartConfigElement mpConfig = new MultipartConfigElement("/tmp", 1024 * 1024, 1024 * 1024 * 5, 1024 * 1024 * 5 * 5);
Expand All @@ -82,7 +83,7 @@ The library is doing only routing, but it's expected that the application provid
1. multiple Producers which will generate responses based on Content-Type
1. multiple Consumers to convert request body to Java objects based on Content-Type

And that is **the basic idea**: you build your web application with only the components you prefer. There is nothing superfluous.
**The basic idea**: build web application with only the components you prefer.
The Routing Library JAR itself is less then 100KB (+ ~ 40KB _SLF4J_ JAR ; + ~ 100KB _javax.servlet-api_ JAR).

The flow is simple:
Expand All @@ -106,13 +107,10 @@ It uses the embedded [Jetty](https://www.eclipse.org/jetty/) as servlet-containe
and [Dagger 2](https://dagger.dev) for dependency injection and as the basis for the _ComponentProvider_.

More or less there are examples of everything:
Various "simple" requests, JSON, File uploading (multipart/form-data), HTML with Thymeleaf, Scopes.
Various "simple" requests, JSON, File uploading (multipart/form-data), HTML with Thymeleaf, Scopes, parameters validation with javax.validation

## Features

## Developing status
It's full functional.

The library is supporting:
1. @Path for class and/or method (regular expressions support; routing priority like in JAX-RS)
1. @GET @POST and so on
1. Types converting for:
Expand All @@ -135,12 +133,12 @@ The library is supporting:
1. application/json: _SimpleGsonConsumer_ (**json-gson** module)
1. inject HttpServletRequest, HttpServletResponse, HttpSession, ServletContext in method parameters
1. _Response_ class to manually construct response.
1. ParametersValidator interface to integrate additional validations e.g. _javax.validation_
1. Implemetation example is **validation-javax** module

## LICENSE

Comming a bit later
1. Java Bean Validation Annotations (JSR 380) for parameters (as additional module)
1. ?

net.cactusthorn.routing is released under the BSD license. See LICENSE file included for the details.



2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>net.cactusthorn.routing</groupId>
<artifactId>root</artifactId>
<version>0.24</version>
<version>0.25</version>
</parent>

<artifactId>core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

Expand All @@ -36,6 +37,8 @@
import net.cactusthorn.routing.convert.ConverterException;
import net.cactusthorn.routing.convert.ConvertersHolder;
import net.cactusthorn.routing.invoke.MethodInvoker;
import net.cactusthorn.routing.validate.ParametersValidator;
import net.cactusthorn.routing.validate.ParametersValidationException;

public class EntryPointScanner {

Expand Down Expand Up @@ -77,7 +80,7 @@ public String template() {
}

public Object invoke(HttpServletRequest req, HttpServletResponse res, ServletContext con, PathValues pathValues)
throws ConverterException {
throws ConverterException, ParametersValidationException {
return methodInvoker.invoke(req, res, con, pathValues);
}

Expand Down Expand Up @@ -109,13 +112,15 @@ public boolean matchContentType(String contenttype) {
private ConvertersHolder convertersHolder;
private ComponentProvider componentProvider;
private Map<ConfigProperty, Object> configProperties;
private Optional<ParametersValidator> validator;

public EntryPointScanner(Collection<Class<?>> classes, ComponentProvider componentProvider, ConvertersHolder convertersHolder,
Map<ConfigProperty, Object> configProperties) {
Map<ConfigProperty, Object> configProperties, Optional<ParametersValidator> validator) {
this.classes.addAll(classes);
this.componentProvider = componentProvider;
this.convertersHolder = convertersHolder;
this.configProperties = configProperties;
this.validator = validator;
}

public Map<Class<? extends Annotation>, List<EntryPoint>> scan() {
Expand Down Expand Up @@ -144,7 +149,7 @@ public Map<Class<? extends Annotation>, List<EntryPoint>> scan() {
String template = findTemplate(method);

MethodInvoker methodInvoker = new MethodInvoker(clazz, method, componentProvider, convertersHolder, contentType,
configProperties);
configProperties, validator);

EntryPoint entryPoint = new EntryPoint(pathTemplate, produces, template, contentType, methodInvoker);
entryPoints.get(type).add(entryPoint);
Expand Down
23 changes: 20 additions & 3 deletions core/src/main/java/net/cactusthorn/routing/RoutingConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import java.util.List;
import java.util.Map;
import java.util.Optional;

import net.cactusthorn.routing.EntryPointScanner.EntryPoint;
import net.cactusthorn.routing.convert.Converter;
import net.cactusthorn.routing.convert.ConvertersHolder;
import net.cactusthorn.routing.producer.Producer;
import net.cactusthorn.routing.producer.TextPlainProducer;
import net.cactusthorn.routing.validate.ParametersValidator;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
Expand Down Expand Up @@ -46,20 +48,24 @@ public Object ddefault() {

private Map<ConfigProperty, Object> configProperties;

private ParametersValidator validator;

// @formatter:off
private RoutingConfig(
ComponentProvider componentProvider,
Map<Class<? extends Annotation>,
List<EntryPoint>> entryPoints,
Map<String, Producer> producers,
Map<String, Consumer> consumers,
Map<ConfigProperty, Object> configProperties) {
Map<ConfigProperty, Object> configProperties,
ParametersValidator validator) {

this.componentProvider = componentProvider;
this.entryPoints = entryPoints;
this.producers = producers;
this.consumers = consumers;
this.configProperties = configProperties;
this.validator = validator;
}
// @formatter:off

Expand Down Expand Up @@ -87,6 +93,10 @@ public Map<ConfigProperty, Object> properties() {
return configProperties;
}

public Optional<ParametersValidator> validator() {
return Optional.ofNullable(validator);
}

public static final class Builder {

private ComponentProvider componentProvider;
Expand All @@ -101,6 +111,8 @@ public static final class Builder {

private final Map<ConfigProperty, Object> configProperties = new HashMap<>();

private ParametersValidator validator;

private Builder(ComponentProvider componentProvider) {
if (componentProvider == null) {
throw new IllegalArgumentException("ComponentProvider can not be null");
Expand Down Expand Up @@ -155,18 +167,23 @@ public Builder setDefaultRequestCharacterEncoding(String encoding) {
return this;
}

public Builder setParametersValidator(ParametersValidator parametersValidator) {
validator = parametersValidator;
return this;
}

public RoutingConfig build() {

Map<String, Producer> unmodifiableProducers = Collections.unmodifiableMap(producers);
Map<String, Consumer> unmodifiableConsumers = Collections.unmodifiableMap(consumers);
Map<ConfigProperty, Object> unmodifiableConfigProperties = Collections.unmodifiableMap(configProperties);

EntryPointScanner scanner = new EntryPointScanner(entryPointClasses, componentProvider, convertersHolder,
unmodifiableConfigProperties);
unmodifiableConfigProperties, Optional.ofNullable(validator));
Map<Class<? extends Annotation>, List<EntryPoint>> entryPoints = scanner.scan();

return new RoutingConfig(componentProvider, Collections.unmodifiableMap(entryPoints), unmodifiableProducers,
unmodifiableConsumers, unmodifiableConfigProperties);
unmodifiableConsumers, unmodifiableConfigProperties, validator);
}
}
}
16 changes: 9 additions & 7 deletions core/src/main/java/net/cactusthorn/routing/RoutingServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.servlet.*;
import javax.servlet.http.*;
Expand All @@ -18,6 +19,8 @@
import net.cactusthorn.routing.annotation.*;
import net.cactusthorn.routing.convert.ConverterException;
import net.cactusthorn.routing.producer.Producer;
import net.cactusthorn.routing.validate.ParametersValidationException;
import net.cactusthorn.routing.validate.ParametersValidator;

public class RoutingServlet extends HttpServlet {

Expand All @@ -32,6 +35,7 @@ public class RoutingServlet extends HttpServlet {
private transient ComponentProvider componentProvider;
private transient String responseCharacterEncoding;
private transient String defaultRequestCharacterEncoding;
private transient Optional<ParametersValidator> parametersValidator;

public RoutingServlet(RoutingConfig config) {
super();
Expand All @@ -41,6 +45,7 @@ public RoutingServlet(RoutingConfig config) {
consumers = config.consumers();
responseCharacterEncoding = (String) config.properties().get(ConfigProperty.RESPONSE_CHARACTER_ENCODING);
defaultRequestCharacterEncoding = (String) config.properties().get(ConfigProperty.DEFAULT_REQUEST_CHARACTER_ENCODING);
parametersValidator = config.validator();
}

@Override //
Expand All @@ -50,6 +55,7 @@ public void init() throws ServletException {
componentProvider.init(servletContext);
producers.values().forEach(p -> p.init(servletContext, componentProvider));
consumers.values().forEach(p -> p.init(servletContext, componentProvider));
parametersValidator.ifPresent(v -> v.init(servletContext, componentProvider));
}

@Override //
Expand Down Expand Up @@ -107,13 +113,9 @@ private void process(HttpServletRequest req, HttpServletResponse resp, List<Entr
Object result = entryPoint.invoke(req, resp, servletContext, values);
produce(req, resp, entryPoint, result);
} catch (ConverterException ce) {
// the inability to convert some parameter(s) is interpreted as a path not found
if (LOG.isTraceEnabled()) {
LOG.trace("", ce);
} else if (LOG.isDebugEnabled()) {
LOG.debug("{} : converterException: {}", req.getPathInfo(), ce.getMessage());
}
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Not Found");
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, ce.getMessage());
} catch (ParametersValidationException ve) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, ve.getMessage());
}
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
Expand All @@ -16,6 +17,8 @@
import net.cactusthorn.routing.RoutingConfig.ConfigProperty;
import net.cactusthorn.routing.PathTemplate.PathValues;
import net.cactusthorn.routing.convert.*;
import net.cactusthorn.routing.validate.ParametersValidator;
import net.cactusthorn.routing.validate.ParametersValidationException;

public final class MethodInvoker {

Expand All @@ -29,12 +32,15 @@ public final class MethodInvoker {

private Map<ConfigProperty, Object> configProperties;

private Optional<ParametersValidator> validator;

public MethodInvoker(Class<?> clazz, Method method, ComponentProvider componentProvider, ConvertersHolder convertersHolder,
String contentType, Map<ConfigProperty, Object> configProperties) {
String contentType, Map<ConfigProperty, Object> configProperties, Optional<ParametersValidator> validator) {
this.clazz = clazz;
this.method = method;
this.componentProvider = componentProvider;
this.configProperties = configProperties;
this.validator = validator;
for (Parameter parameter : method.getParameters()) {
if (parameter.isSynthetic()) {
continue;
Expand All @@ -44,7 +50,7 @@ public MethodInvoker(Class<?> clazz, Method method, ComponentProvider componentP
}

public Object invoke(HttpServletRequest req, HttpServletResponse res, ServletContext con, PathValues pathValues)
throws ConverterException {
throws ConverterException, ParametersValidationException {

Object object = componentProvider.provide(clazz, req);
RequestData requestData;
Expand All @@ -54,12 +60,7 @@ public Object invoke(HttpServletRequest req, HttpServletResponse res, ServletCon
requestData = new RequestData(pathValues);
}

Object[] values;
if (parameters.size() == 0) {
values = new Object[0];
} else {
values = new Object[parameters.size()];
}
Object[] values = parameters.size() == 0 ? new Object[0] : new Object[parameters.size()];

for (int i = 0; i < parameters.size(); i++) {
MethodParameter parameter = parameters.get(i);
Expand All @@ -70,6 +71,10 @@ public Object invoke(HttpServletRequest req, HttpServletResponse res, ServletCon
}
}

if (validator.isPresent()) {
validator.get().validate(object, method, values);
}

try {
return method.invoke(object, values);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package net.cactusthorn.routing.validate;

public class ParametersValidationException extends Exception {

private static final long serialVersionUID = 0L;

public ParametersValidationException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.cactusthorn.routing.validate;

import java.lang.reflect.Method;

import javax.servlet.ServletContext;

import net.cactusthorn.routing.ComponentProvider;

public interface ParametersValidator {

default void init(ServletContext servletContext, ComponentProvider componentProvider) {
}

void validate(Object object, Method method, Object[] parameters) throws ParametersValidationException;
}
Loading

0 comments on commit bc214de

Please sign in to comment.