Repository: tipsy/javalin
Pull request #1269 (2021-06-07 17:34:14)

[core] Change Set<Role> to Role... (varargs)

tipsy: @Playacem would you prefer if I wait until you finish your PR before merging this?
Playacem: @tipsy The current changes seem easy enough for manual merging. You can go ahead from my side.

I prefer Collections over varargs. We could go a similiar route as the `List.of` / `Set.of` functions in java by providing overloads for the first few number of roles. I am not a user of the access manager however, so I am unsure how many roles are typically passed.
tipsy: > I am not a user of the access manager however, so I am unsure how many roles are typically passed.

In most projects that I have seen, it's 1. I personally have one project that uses 1-2 (VIEW/EDIT MODEL) and ~20 that use 1. The reason I used a Set initially was in case there would be another overload for the routes, but I don't think that will happen.

> @tipsy The current changes seem easy enough for manual merging. You can go ahead from my side.

Thanks !

Patch:
diff --git a/javalin-openapi/src/main/java/io/javalin/plugin/openapi/OpenApiOptions.kt b/javalin-openapi/src/main/java/io/javalin/plugin/openapi/OpenApiOptions.kt
index ad6cf814..e7bec01f 100644
--- a/javalin-openapi/src/main/java/io/javalin/plugin/openapi/OpenApiOptions.kt
+++ b/javalin-openapi/src/main/java/io/javalin/plugin/openapi/OpenApiOptions.kt
@@ -2,7 +2,7 @@ package io.javalin.plugin.openapi
 
 import com.fasterxml.jackson.databind.ObjectMapper
 import io.javalin.core.event.HandlerMetaInfo
-import io.javalin.core.security.Role
+import io.javalin.core.security.RouteRole
 import io.javalin.http.Context
 import io.javalin.http.Handler
 import io.javalin.http.HandlerType
@@ -22,7 +22,7 @@ import io.swagger.v3.oas.models.info.Info
 class OpenApiOptions constructor(val initialConfigurationCreator: InitialConfigurationCreator) {
     /** If not null, creates a GET route to get the schema as a json */
     var path: String? = null
-    var roles: Set<Role> = setOf()
+    var roles: Set<RouteRole> = setOf()
     /**
      * If not null, creates a GET route to the swagger ui
      * @see <a href="https://swagger.io/tools/swagger-ui/">https://swagger.io/tools/swagger-ui/</a>
@@ -101,7 +101,7 @@ class OpenApiOptions constructor(val initialConfigurationCreator: InitialConfigu
 
     fun reDoc(value: ReDocOptions) = apply { reDoc = value }
 
-    fun roles(value: Set<Role>) = apply { roles = value }
+    fun roles(vararg value: RouteRole) = apply { roles = value.toSet() }
 
     fun defaultDocumentation(value: DefaultDocumentation) = apply { default = value }
     fun defaultDocumentation(apply: (documentation: OpenApiDocumentation) -> Unit) = apply {
diff --git a/javalin-openapi/src/main/java/io/javalin/plugin/openapi/OpenApiPlugin.kt b/javalin-openapi/src/main/java/io/javalin/plugin/openapi/OpenApiPlugin.kt
index 93309cc9..ec3e31b7 100644
--- a/javalin-openapi/src/main/java/io/javalin/plugin/openapi/OpenApiPlugin.kt
+++ b/javalin-openapi/src/main/java/io/javalin/plugin/openapi/OpenApiPlugin.kt
@@ -50,16 +50,16 @@ class OpenApiPlugin(private vararg val options: OpenApiOptions) : Plugin, Plugin
             }
 
             options.path?.let { path ->
-                app.get(path, handlerMap[path]!!, options.roles)
+                app.get(path, handlerMap[path]!!, *options.roles.toTypedArray())
 
                 options.swagger?.let {
                     Util.assertWebjarInstalled(OptionalDependency.SWAGGERUI)
-                    app.get(it.path, SwaggerRenderer(options), options.roles)
+                    app.get(it.path, SwaggerRenderer(options), *options.roles.toTypedArray())
                 }
 
                 options.reDoc?.let {
                     Util.assertWebjarInstalled(OptionalDependency.REDOC)
-                    app.get(it.path, ReDocRenderer(options), options.roles)
+                    app.get(it.path, ReDocRenderer(options), *options.roles.toTypedArray())
                 }
 
                 if (options.swagger != null || options.reDoc != null) {
@@ -67,7 +67,6 @@ class OpenApiPlugin(private vararg val options: OpenApiOptions) : Plugin, Plugin
                 }
             }
 
-
         }
 
     }
diff --git a/javalin/src/main/java/io/javalin/Javalin.java b/javalin/src/main/java/io/javalin/Javalin.java
index 20e025c9..2459e486 100644
--- a/javalin/src/main/java/io/javalin/Javalin.java
+++ b/javalin/src/main/java/io/javalin/Javalin.java
@@ -18,7 +18,7 @@ import io.javalin.core.event.HandlerMetaInfo;
 import io.javalin.core.event.JavalinEvent;
 import io.javalin.core.event.WsHandlerMetaInfo;
 import io.javalin.core.security.AccessManager;
-import io.javalin.core.security.Role;
+import io.javalin.core.security.RouteRole;
 import io.javalin.core.util.JavalinLogger;
 import io.javalin.core.util.Util;
 import io.javalin.core.validation.JavalinValidation;
@@ -34,6 +34,7 @@ import io.javalin.websocket.JavalinWsServlet;
 import io.javalin.websocket.WsConfig;
 import io.javalin.websocket.WsExceptionHandler;
 import io.javalin.websocket.WsHandlerType;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.function.Consumer;
@@ -306,15 +307,16 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public Javalin addHandler(@NotNull HandlerType handlerType, @NotNull String path, @NotNull Handler handler, @NotNull Set<Role> roles) {
+    public Javalin addHandler(@NotNull HandlerType handlerType, @NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
         if (Util.isNonSubPathWildcard(path)) { // TODO: This should probably be made part of the actual path matching
             // split into two handlers: one exact, and one sub-path with wildcard
             String basePath = path.substring(0, path.length() - 1);
             addHandler(handlerType, basePath, handler, roles);
             return addHandler(handlerType, basePath + "/*", handler, roles);
         }
-        servlet.addHandler(handlerType, path, handler, roles);
-        eventManager.fireHandlerAddedEvent(new HandlerMetaInfo(handlerType, Util.prefixContextPath(servlet.getConfig().contextPath, path), handler, roles));
+        Set<RouteRole> roleSet = new HashSet<>(Arrays.asList(roles));
+        servlet.addHandler(handlerType, path, handler, roleSet);
+        eventManager.fireHandlerAddedEvent(new HandlerMetaInfo(handlerType, Util.prefixContextPath(servlet.getConfig().contextPath, path), handler, roleSet));
         return this;
     }
 
@@ -325,7 +327,7 @@ public class Javalin {
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
     public Javalin addHandler(@NotNull HandlerType httpMethod, @NotNull String path, @NotNull Handler handler) {
-        return addHandler(httpMethod, path, handler, new HashSet<>()); // no roles set for this route (open to everyone with default access manager)
+        return addHandler(httpMethod, path, handler, new RouteRole[0]); // no roles set for this route (open to everyone with default access manager)
     }
 
     /**
@@ -402,8 +404,8 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public Javalin get(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        return addHandler(HandlerType.GET, path, handler, permittedRoles);
+    public Javalin get(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        return addHandler(HandlerType.GET, path, handler, roles);
     }
 
     /**
@@ -413,8 +415,8 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public Javalin post(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        return addHandler(HandlerType.POST, path, handler, permittedRoles);
+    public Javalin post(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        return addHandler(HandlerType.POST, path, handler, roles);
     }
 
     /**
@@ -424,8 +426,8 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public Javalin put(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        return addHandler(HandlerType.PUT, path, handler, permittedRoles);
+    public Javalin put(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        return addHandler(HandlerType.PUT, path, handler, roles);
     }
 
     /**
@@ -435,8 +437,8 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public Javalin patch(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        return addHandler(HandlerType.PATCH, path, handler, permittedRoles);
+    public Javalin patch(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        return addHandler(HandlerType.PATCH, path, handler, roles);
     }
 
     /**
@@ -446,8 +448,8 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public Javalin delete(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        return addHandler(HandlerType.DELETE, path, handler, permittedRoles);
+    public Javalin delete(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        return addHandler(HandlerType.DELETE, path, handler, roles);
     }
 
     /**
@@ -457,8 +459,8 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public Javalin head(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        return addHandler(HandlerType.HEAD, path, handler, permittedRoles);
+    public Javalin head(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        return addHandler(HandlerType.HEAD, path, handler, roles);
     }
 
     /**
@@ -468,8 +470,8 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public Javalin options(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        return addHandler(HandlerType.OPTIONS, path, handler, permittedRoles);
+    public Javalin options(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        return addHandler(HandlerType.OPTIONS, path, handler, roles);
     }
 
     // ********************************************************************************************
@@ -480,15 +482,15 @@ public class Javalin {
      * Adds a lambda handler for a Server Sent Event connection on the specified path.
      */
     public Javalin sse(@NotNull String path, @NotNull Consumer<SseClient> client) {
-        return sse(path, client, new HashSet<>());
+        return sse(path, client, new RouteRole[0]);
     }
 
     /**
      * Adds a lambda handler for a Server Sent Event connection on the specified path.
      * Requires an access manager to be set on the instance.
      */
-    public Javalin sse(@NotNull String path, @NotNull Consumer<SseClient> client, @NotNull Set<Role> permittedRoles) {
-        return get(path, new SseHandler(client), permittedRoles);
+    public Javalin sse(@NotNull String path, @NotNull Consumer<SseClient> client, @NotNull RouteRole... roles) {
+        return get(path, new SseHandler(client), roles);
     }
 
     // ********************************************************************************************
@@ -549,9 +551,10 @@ public class Javalin {
      * Adds a specific WebSocket handler for the given path to the instance.
      * Requires an access manager to be set on the instance.
      */
-    private Javalin addWsHandler(@NotNull WsHandlerType handlerType, @NotNull String path, @NotNull Consumer<WsConfig> wsConfig, @NotNull Set<Role> roles) {
-        wsServlet.addHandler(handlerType, path, wsConfig, roles);
-        eventManager.fireWsHandlerAddedEvent(new WsHandlerMetaInfo(handlerType, Util.prefixContextPath(servlet.getConfig().contextPath, path), wsConfig, roles));
+    private Javalin addWsHandler(@NotNull WsHandlerType handlerType, @NotNull String path, @NotNull Consumer<WsConfig> wsConfig, @NotNull RouteRole... roles) {
+        Set<RouteRole> roleSet = new HashSet<>(Arrays.asList(roles));
+        wsServlet.addHandler(handlerType, path, wsConfig, roleSet);
+        eventManager.fireWsHandlerAddedEvent(new WsHandlerMetaInfo(handlerType, Util.prefixContextPath(servlet.getConfig().contextPath, path), wsConfig, roleSet));
         return this;
     }
 
@@ -559,7 +562,7 @@ public class Javalin {
      * Adds a specific WebSocket handler for the given path to the instance.
      */
     private Javalin addWsHandler(@NotNull WsHandlerType handlerType, @NotNull String path, @NotNull Consumer<WsConfig> wsConfig) {
-        return addWsHandler(handlerType, path, wsConfig, new HashSet<>());
+        return addWsHandler(handlerType, path, wsConfig, new RouteRole[0]);
     }
 
     /**
@@ -568,7 +571,7 @@ public class Javalin {
      * @see <a href="https://javalin.io/documentation#websockets">WebSockets in docs</a>
      */
     public Javalin ws(@NotNull String path, @NotNull Consumer<WsConfig> ws) {
-        return ws(path, ws, new HashSet<>());
+        return ws(path, ws, new RouteRole[0]);
     }
 
     /**
@@ -578,8 +581,8 @@ public class Javalin {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#websockets">WebSockets in docs</a>
      */
-    public Javalin ws(@NotNull String path, @NotNull Consumer<WsConfig> ws, @NotNull Set<Role> permittedRoles) {
-        return addWsHandler(WsHandlerType.WEBSOCKET, path, ws, permittedRoles);
+    public Javalin ws(@NotNull String path, @NotNull Consumer<WsConfig> ws, @NotNull RouteRole... roles) {
+        return addWsHandler(WsHandlerType.WEBSOCKET, path, ws, roles);
     }
 
     /**
diff --git a/javalin/src/main/java/io/javalin/apibuilder/ApiBuilder.java b/javalin/src/main/java/io/javalin/apibuilder/ApiBuilder.java
index 9813ac66..26604c12 100644
--- a/javalin/src/main/java/io/javalin/apibuilder/ApiBuilder.java
+++ b/javalin/src/main/java/io/javalin/apibuilder/ApiBuilder.java
@@ -8,16 +8,14 @@ package io.javalin.apibuilder;
 
 import io.javalin.Javalin;
 import io.javalin.core.security.AccessManager;
-import io.javalin.core.security.Role;
+import io.javalin.core.security.RouteRole;
 import io.javalin.http.Handler;
 import io.javalin.http.sse.SseClient;
 import io.javalin.websocket.WsConfig;
 import java.util.ArrayDeque;
 import java.util.Arrays;
 import java.util.Deque;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
 import java.util.function.Consumer;
 import org.jetbrains.annotations.NotNull;
 
@@ -85,8 +83,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void get(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().get(prefixPath(path), handler, permittedRoles);
+    public static void get(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().get(prefixPath(path), handler, roles);
     }
 
     /**
@@ -106,8 +104,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void get(@NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().get(prefixPath(""), handler, permittedRoles);
+    public static void get(@NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().get(prefixPath(""), handler, roles);
     }
 
     /**
@@ -127,8 +125,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void post(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().post(prefixPath(path), handler, permittedRoles);
+    public static void post(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().post(prefixPath(path), handler, roles);
     }
 
     /**
@@ -148,8 +146,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void post(@NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().post(prefixPath(""), handler, permittedRoles);
+    public static void post(@NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().post(prefixPath(""), handler, roles);
     }
 
     /**
@@ -169,8 +167,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void put(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().put(prefixPath(path), handler, permittedRoles);
+    public static void put(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().put(prefixPath(path), handler, roles);
     }
 
     /**
@@ -190,8 +188,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void put(@NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().put(prefixPath(""), handler, permittedRoles);
+    public static void put(@NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().put(prefixPath(""), handler, roles);
     }
 
     /**
@@ -211,8 +209,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void patch(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().patch(prefixPath(path), handler, permittedRoles);
+    public static void patch(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().patch(prefixPath(path), handler, roles);
     }
 
     /**
@@ -232,8 +230,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void patch(@NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().patch(prefixPath(""), handler, permittedRoles);
+    public static void patch(@NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().patch(prefixPath(""), handler, roles);
     }
 
     /**
@@ -253,8 +251,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void delete(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().delete(prefixPath(path), handler, permittedRoles);
+    public static void delete(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().delete(prefixPath(path), handler, roles);
     }
 
     /**
@@ -274,8 +272,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void delete(@NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().delete(prefixPath(""), handler, permittedRoles);
+    public static void delete(@NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().delete(prefixPath(""), handler, roles);
     }
 
     /**
@@ -295,8 +293,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void head(@NotNull String path, @NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().head(prefixPath(path), handler, permittedRoles);
+    public static void head(@NotNull String path, @NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().head(prefixPath(path), handler, roles);
     }
 
     /**
@@ -316,8 +314,8 @@ public class ApiBuilder {
      * @see AccessManager
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void head(@NotNull Handler handler, @NotNull Set<Role> permittedRoles) {
-        staticInstance().head(prefixPath(""), handler, permittedRoles);
+    public static void head(@NotNull Handler handler, @NotNull RouteRole... roles) {
+        staticInstance().head(prefixPath(""), handler, roles);
     }
 
     // ********************************************************************************************
@@ -384,8 +382,8 @@ public class ApiBuilder {
      *
      * @see <a href="https://javalin.io/documentation#websockets">WebSockets in docs</a>
      */
-    public static void ws(@NotNull String path, @NotNull Consumer<WsConfig> ws, @NotNull Set<Role> permittedRoles) {
-        staticInstance().ws(prefixPath(path), ws, permittedRoles);
+    public static void ws(@NotNull String path, @NotNull Consumer<WsConfig> ws, @NotNull RouteRole... roles) {
+        staticInstance().ws(prefixPath(path), ws, roles);
     }
 
     /**
@@ -404,8 +402,8 @@ public class ApiBuilder {
      *
      * @see <a href="https://javalin.io/documentation#websockets">WebSockets in docs</a>
      */
-    public static void ws(@NotNull Consumer<WsConfig> ws, @NotNull Set<Role> permittedRoles) {
-        staticInstance().ws(prefixPath(""), ws, permittedRoles);
+    public static void ws(@NotNull Consumer<WsConfig> ws, @NotNull RouteRole... roles) {
+        staticInstance().ws(prefixPath(""), ws, roles);
     }
 
     /**
@@ -448,16 +446,16 @@ public class ApiBuilder {
         staticInstance().sse(prefixPath(path), client);
     }
 
-    public static void sse(@NotNull String path, @NotNull Consumer<SseClient> client, @NotNull Set<Role> permittedRoles) {
-        staticInstance().sse(prefixPath(path), client, permittedRoles);
+    public static void sse(@NotNull String path, @NotNull Consumer<SseClient> client, @NotNull RouteRole... roles) {
+        staticInstance().sse(prefixPath(path), client, roles);
     }
 
     public static void sse(@NotNull Consumer<SseClient> client) {
         staticInstance().sse(prefixPath(""), client);
     }
 
-    public static void sse(@NotNull Consumer<SseClient> client, @NotNull Set<Role> permittedRoles) {
-        staticInstance().sse(prefixPath(""), client, permittedRoles);
+    public static void sse(@NotNull Consumer<SseClient> client, @NotNull RouteRole... roles) {
+        staticInstance().sse(prefixPath(""), client, roles);
     }
 
     // ********************************************************************************************
@@ -471,7 +469,7 @@ public class ApiBuilder {
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
     public static void crud(@NotNull CrudHandler crudHandler) {
-        crud("", crudHandler, new HashSet<>());
+        crud("", crudHandler, new RouteRole[0]);
     }
 
     /**
@@ -480,8 +478,8 @@ public class ApiBuilder {
      *
      * @see <a href="https://javalin.io/documentation#handlers">Handlers in docs</a>
      */
-    public static void crud(@NotNull CrudHandler crudHandler, @NotNull Set<Role> permittedRoles) {
-        crud("", crudHandler, permittedRoles);
+    public static void crud(@NotNull CrudHandler crudHandler, @NotNull RouteRole... roles) {
+        crud("", crudHandler, roles);
     }
 
     /**
@@ -491,7 +489,7 @@ public class ApiBuilder {
      * @see CrudHandler
      */
     public static void crud(@NotNull String path, @NotNull CrudHandler crudHandler) {
-        crud(path, crudHandler, new HashSet<>());
+        crud(path, crudHandler, new RouteRole[0]);
     }
 
     /**
@@ -500,7 +498,7 @@ public class ApiBuilder {
      *
      * @see CrudHandler
      */
-    public static void crud(@NotNull String path, @NotNull CrudHandler crudHandler, @NotNull Set<Role> permittedRoles) {
+    public static void crud(@NotNull String path, @NotNull CrudHandler crudHandler, @NotNull RouteRole... roles) {
         String fullPath = prefixPath(path);
         String[] subPaths = Arrays.stream(fullPath.split("/")).filter(it -> !it.isEmpty()).toArray(String[]::new);
         if (subPaths.length < 2) {
@@ -514,10 +512,10 @@ public class ApiBuilder {
             throw new IllegalArgumentException("CrudHandler requires a resource base at the beginning of the provided path, e.g. '/users/:user-id'");
         }
         Map<CrudFunction, Handler> crudFunctions = CrudHandlerKt.getCrudFunctions(crudHandler, resourceId);
-        staticInstance().get(fullPath, crudFunctions.get(CrudFunction.GET_ONE), permittedRoles);
-        staticInstance().get(fullPath.replace(resourceId, ""), crudFunctions.get(CrudFunction.GET_ALL), permittedRoles);
-        staticInstance().post(fullPath.replace(resourceId, ""), crudFunctions.get(CrudFunction.CREATE), permittedRoles);
-        staticInstance().patch(fullPath, crudFunctions.get(CrudFunction.UPDATE), permittedRoles);
-        staticInstance().delete(fullPath, crudFunctions.get(CrudFunction.DELETE), permittedRoles);
+        staticInstance().get(fullPath, crudFunctions.get(CrudFunction.GET_ONE), roles);
+        staticInstance().get(fullPath.replace(resourceId, ""), crudFunctions.get(CrudFunction.GET_ALL), roles);
+        staticInstance().post(fullPath.replace(resourceId, ""), crudFunctions.get(CrudFunction.CREATE), roles);
+        staticInstance().patch(fullPath, crudFunctions.get(CrudFunction.UPDATE), roles);
+        staticInstance().delete(fullPath, crudFunctions.get(CrudFunction.DELETE), roles);
     }
 }
diff --git a/javalin/src/main/java/io/javalin/core/event/EventManager.kt b/javalin/src/main/java/io/javalin/core/event/EventManager.kt
index d2d3528f..e6fd8179 100644
--- a/javalin/src/main/java/io/javalin/core/event/EventManager.kt
+++ b/javalin/src/main/java/io/javalin/core/event/EventManager.kt
@@ -6,7 +6,7 @@
 
 package io.javalin.core.event
 
-import io.javalin.core.security.Role
+import io.javalin.core.security.RouteRole
 import io.javalin.http.HandlerType
 import io.javalin.websocket.WsHandlerType
 import java.util.*
@@ -22,5 +22,5 @@ class EventManager {
 }
 
 enum class JavalinEvent { SERVER_STARTING, SERVER_STARTED, SERVER_START_FAILED, SERVER_STOPPING, SERVER_STOPPED }
-data class HandlerMetaInfo(val httpMethod: HandlerType, val path: String, val handler: Any, val roles: Set<Role>)
-data class WsHandlerMetaInfo(val handlerType: WsHandlerType, val path: String, val handler: Any, val roles: Set<Role>)
+data class HandlerMetaInfo(val httpMethod: HandlerType, val path: String, val handler: Any, val roles: Set<RouteRole>)
+data class WsHandlerMetaInfo(val handlerType: WsHandlerType, val path: String, val handler: Any, val roles: Set<RouteRole>)
diff --git a/javalin/src/main/java/io/javalin/core/security/AccessManager.java b/javalin/src/main/java/io/javalin/core/security/AccessManager.java
index cdaf55cd..f6f3ef8d 100644
--- a/javalin/src/main/java/io/javalin/core/security/AccessManager.java
+++ b/javalin/src/main/java/io/javalin/core/security/AccessManager.java
@@ -17,11 +17,11 @@ import org.jetbrains.annotations.NotNull;
  * It's only enabled for endpoints if a list of roles is provided.
  * Ex: get("/secured", SecuredController::get, roles(LOGGED_IN));
  *
- * @see Role
- * @see io.javalin.Javalin#addHandler(HandlerType, String, Handler, Set)
+ * @see RouteRole
+ * @see io.javalin.Javalin#addHandler(HandlerType, String, Handler, RouteRole...)
  * @see <a href="https://javalin.io/documentation#access-manager">Access manager in docs</a>
  */
 @FunctionalInterface
 public interface AccessManager {
-    void manage(@NotNull Handler handler, @NotNull Context ctx, @NotNull Set<Role> permittedRoles) throws Exception;
+    void manage(@NotNull Handler handler, @NotNull Context ctx, @NotNull Set<RouteRole> routeRoles) throws Exception;
 }
diff --git a/javalin/src/main/java/io/javalin/core/security/Role.java b/javalin/src/main/java/io/javalin/core/security/RouteRole.java
similarity index 76%
rename from javalin/src/main/java/io/javalin/core/security/Role.java
rename to javalin/src/main/java/io/javalin/core/security/RouteRole.java
index 51e8fe77..9cc11d1e 100644
--- a/javalin/src/main/java/io/javalin/core/security/Role.java
+++ b/javalin/src/main/java/io/javalin/core/security/RouteRole.java
@@ -7,9 +7,9 @@
 package io.javalin.core.security;
 
 /**
- * Empty interface for roles used in {@link AccessManager}.
+ * Marker interface for roles used in {@link AccessManager}.
  *
  * @see <a href="https://javalin.io/documentation#access-manager">Access manager in docs</a>
  */
-public interface Role {
+public interface RouteRole {
 }
diff --git a/javalin/src/main/java/io/javalin/core/security/SecurityUtil.kt b/javalin/src/main/java/io/javalin/core/security/SecurityUtil.kt
index 831201c6..faa9e314 100644
--- a/javalin/src/main/java/io/javalin/core/security/SecurityUtil.kt
+++ b/javalin/src/main/java/io/javalin/core/security/SecurityUtil.kt
@@ -13,11 +13,8 @@ import io.javalin.http.util.ContextUtil.isLocalhost
 object SecurityUtil {
 
     @JvmStatic
-    fun roles(vararg roles: Role) = setOf(*roles)
-
-    @JvmStatic
-    fun noopAccessManager(handler: Handler, ctx: Context, permittedRoles: Set<Role>) {
-        if (permittedRoles.isNotEmpty()) {
+    fun noopAccessManager(handler: Handler, ctx: Context, roles: Set<RouteRole>) {
+        if (roles.isNotEmpty()) {
             throw IllegalStateException("No access manager configured. Add an access manager using 'Javalin.create(c -> c.accessManager(...))'.")
         }
         handler.handle(ctx)
diff --git a/javalin/src/main/java/io/javalin/core/util/RouteOverviewPlugin.kt b/javalin/src/main/java/io/javalin/core/util/RouteOverviewPlugin.kt
index 67b7436c..cc7edf1d 100644
--- a/javalin/src/main/java/io/javalin/core/util/RouteOverviewPlugin.kt
+++ b/javalin/src/main/java/io/javalin/core/util/RouteOverviewPlugin.kt
@@ -3,11 +3,11 @@ package io.javalin.core.util
 import io.javalin.Javalin
 import io.javalin.core.plugin.Plugin
 import io.javalin.core.plugin.PluginLifecycleInit
-import io.javalin.core.security.Role
+import io.javalin.core.security.RouteRole
 
 class RouteOverviewPlugin(val config: RouteOverviewConfig) : Plugin, PluginLifecycleInit {
     @JvmOverloads
-    constructor(path: String, roles: Set<Role> = setOf()) : this(RouteOverviewConfig(path, roles))
+    constructor(path: String, vararg roles: RouteRole = arrayOf()) : this(RouteOverviewConfig(path, roles.toSet()))
 
     lateinit var renderer: RouteOverviewRenderer
 
@@ -16,6 +16,6 @@ class RouteOverviewPlugin(val config: RouteOverviewConfig) : Plugin, PluginLifec
     }
 
     override fun apply(app: Javalin) {
-        app.get(this.config.path, renderer, this.config.roles)
+        app.get(this.config.path, renderer, *this.config.roles.toTypedArray())
     }
 }
diff --git a/javalin/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt b/javalin/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
index d73eddc7..748de5f7 100644
--- a/javalin/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
+++ b/javalin/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
@@ -10,7 +10,7 @@ import io.javalin.Javalin
 import io.javalin.apibuilder.CrudFunctionHandler
 import io.javalin.core.event.HandlerMetaInfo
 import io.javalin.core.event.WsHandlerMetaInfo
-import io.javalin.core.security.Role
+import io.javalin.core.security.RouteRole
 import io.javalin.http.Context
 import io.javalin.http.Handler
 import io.javalin.plugin.openapi.annotations.ContentType
@@ -18,7 +18,7 @@ import io.javalin.plugin.openapi.annotations.OpenApi
 import io.javalin.plugin.openapi.annotations.OpenApiContent
 import io.javalin.plugin.openapi.annotations.OpenApiResponse
 
-data class RouteOverviewConfig(val path: String, val roles: Set<Role>)
+data class RouteOverviewConfig(val path: String, val roles: Set<RouteRole>)
 
 class RouteOverviewRenderer(val app: Javalin) : Handler {
 
diff --git a/javalin/src/main/java/io/javalin/core/validation/BaseValidator.kt b/javalin/src/main/java/io/javalin/core/validation/BaseValidator.kt
index 0c1ede5d..55d1e7b3 100644
--- a/javalin/src/main/java/io/javalin/core/validation/BaseValidator.kt
+++ b/javalin/src/main/java/io/javalin/core/validation/BaseValidator.kt
@@ -8,8 +8,8 @@ package io.javalin.core.validation
 
 typealias Check<T> = (T) -> Boolean
 
-data class Rule<T>(val fieldName: String, val check: Check<T?>, val error: String)
-data class ValidationError<T>(val message: String, val value: T?)
+data class Rule<T>(val fieldName: String, val check: Check<T?>, val error: ValidationError<T>)
+data class ValidationError<T>(val message: String, val args: Map<String, Any?> = mapOf(), var value: T? = null)
 class ValidationException(val errors: Map<String, List<ValidationError<Any>>>) : Exception()
 
 open class BaseValidator<T>(val value: T?, val fieldName: String) {
@@ -18,19 +18,24 @@ open class BaseValidator<T>(val value: T?, val fieldName: String) {
     private val errors by lazy {
         val errors = mutableMapOf<String, MutableList<ValidationError<T>>>()
         if (value == null && this !is NullableValidator) {
-            errors[fieldName] = mutableListOf(ValidationError("NULLCHECK_FAILED", value))
+            errors[fieldName] = mutableListOf(ValidationError("NULLCHECK_FAILED", value = value))
         }
         rules.forEach { rule ->
             if (value != null && !rule.check(value)) {
                 // the same validator can have multiple field names if it's a BodyValidator
                 errors.computeIfAbsent(rule.fieldName) { mutableListOf() }
-                errors[rule.fieldName]!!.add(ValidationError(rule.error, value))
+                errors[rule.fieldName]!!.add(rule.error.also { it.value = value })
             }
         }
         errors.mapValues { it.value.toList() }.toMap() // make immutable
     }
 
     fun addRule(fieldName: String, check: Check<T?>, error: String): BaseValidator<T> {
+        rules.add(Rule(fieldName, check, ValidationError(error)))
+        return this
+    }
+
+    fun addRule(fieldName: String, check: Check<T?>, error: ValidationError<T>): BaseValidator<T> {
         rules.add(Rule(fieldName, check, error))
         return this
     }
diff --git a/javalin/src/main/java/io/javalin/core/validation/BodyValidator.kt b/javalin/src/main/java/io/javalin/core/validation/BodyValidator.kt
index f776ca8a..117c2384 100644
--- a/javalin/src/main/java/io/javalin/core/validation/BodyValidator.kt
+++ b/javalin/src/main/java/io/javalin/core/validation/BodyValidator.kt
@@ -8,7 +8,9 @@ package io.javalin.core.validation
 
 open class BodyValidator<T>(value: T?) : BaseValidator<T>(value, value.fieldName()) {
     fun check(check: Check<T>, error: String) = check(value.fieldName(), check, error)
+    fun check(check: Check<T>, error: ValidationError<T>) = check(value.fieldName(), check, error)
     fun check(fieldName: String, check: Check<T>, error: String) = addRule(fieldName, { check(it!!) }, error) as BodyValidator<T>
+    fun check(fieldName: String, check: Check<T>, error: ValidationError<T>) = addRule(fieldName, { check(it!!) }, error) as BodyValidator<T>
     override fun get(): T = super.get()!!
 }
 
diff --git a/javalin/src/main/java/io/javalin/core/validation/JavalinValidation.kt b/javalin/src/main/java/io/javalin/core/validation/JavalinValidation.kt
index c7e8ef2c..566d7823 100644
--- a/javalin/src/main/java/io/javalin/core/validation/JavalinValidation.kt
+++ b/javalin/src/main/java/io/javalin/core/validation/JavalinValidation.kt
@@ -51,5 +51,6 @@ object JavalinValidation {
     }
 }
 
+
 fun Iterable<Validator<*>>.collectErrors(): Map<String, List<ValidationError<out Any?>>> =
     JavalinValidation.collectErrors(this)
diff --git a/javalin/src/main/java/io/javalin/core/validation/NullableValidator.kt b/javalin/src/main/java/io/javalin/core/validation/NullableValidator.kt
index e667fdb6..599a3da1 100644
--- a/javalin/src/main/java/io/javalin/core/validation/NullableValidator.kt
+++ b/javalin/src/main/java/io/javalin/core/validation/NullableValidator.kt
@@ -8,4 +8,5 @@ package io.javalin.core.validation
 
 open class NullableValidator<T>(value: T?, fieldName: String) : BaseValidator<T>(value, fieldName) {
     fun check(check: Check<T?>, error: String) = addRule(fieldName, check, error) as NullableValidator<T>
+    fun check(check: Check<T?>, error: ValidationError<T>) = addRule(fieldName, check, error) as NullableValidator<T>
 }
diff --git a/javalin/src/main/java/io/javalin/core/validation/Validator.kt b/javalin/src/main/java/io/javalin/core/validation/Validator.kt
index f10db821..0d140501 100644
--- a/javalin/src/main/java/io/javalin/core/validation/Validator.kt
+++ b/javalin/src/main/java/io/javalin/core/validation/Validator.kt
@@ -21,6 +21,8 @@ open class Validator<T>(value: T?, fieldName: String) : BaseValidator<T>(value,
     }
 
     fun check(check: Check<T>, error: String) = addRule(fieldName, { check(it!!) }, error) as Validator<T>
+    fun check(check: Check<T>, error: ValidationError<T>) = addRule(fieldName, { check(it!!) }, error) as Validator<T>
+
     override fun get(): T = super.get()!!
 
     companion object {
@@ -33,7 +35,7 @@ open class Validator<T>(value: T?, fieldName: String) : BaseValidator<T>(value,
                 throw InternalServerErrorResponse()
             }
             JavalinLogger.info("Parameter '${fieldName}' with value '${value}' is not a valid ${clazz.simpleName}")
-            throw ValidationException(mapOf(fieldName to listOf(ValidationError("TYPE_CONVERSION_FAILED", value))))
+            throw ValidationException(mapOf(fieldName to listOf(ValidationError("TYPE_CONVERSION_FAILED", value = value))))
         }
     }
 }
diff --git a/javalin/src/main/java/io/javalin/http/Context.kt b/javalin/src/main/java/io/javalin/http/Context.kt
index 358178fc..b7dcce0e 100644
--- a/javalin/src/main/java/io/javalin/http/Context.kt
+++ b/javalin/src/main/java/io/javalin/http/Context.kt
@@ -130,7 +130,7 @@ open class Context(@JvmField val req: HttpServletRequest, @JvmField val res: Htt
         BodyValidator(JavalinJson.fromJson(body(), clazz))
     } catch (e: Exception) {
         JavalinLogger.info("Couldn't deserialize body to ${clazz.simpleName}", e)
-        throw ValidationException(mapOf(clazz.simpleName to listOf(ValidationError("DESERIALIZATION_FAILED", body()))))
+        throw ValidationException(mapOf(clazz.simpleName to listOf(ValidationError("DESERIALIZATION_FAILED", value = body()))))
     }
 
     /** Reified version of [bodyValidator] */
diff --git a/javalin/src/main/java/io/javalin/http/JavalinServlet.kt b/javalin/src/main/java/io/javalin/http/JavalinServlet.kt
index 5a0057af..86130bfa 100644
--- a/javalin/src/main/java/io/javalin/http/JavalinServlet.kt
+++ b/javalin/src/main/java/io/javalin/http/JavalinServlet.kt
@@ -7,7 +7,7 @@
 package io.javalin.http
 
 import io.javalin.core.JavalinConfig
-import io.javalin.core.security.Role
+import io.javalin.core.security.RouteRole
 import io.javalin.core.util.CorsPlugin
 import io.javalin.core.util.Header
 import io.javalin.core.util.JavalinLogger
@@ -125,7 +125,7 @@ class JavalinServlet(val config: JavalinConfig) : HttpServlet() {
 
     private fun hasGetHandlerMapped(requestUri: String) = matcher.findEntries(HandlerType.GET, requestUri).isNotEmpty()
 
-    fun addHandler(handlerType: HandlerType, path: String, handler: Handler, roles: Set<Role>) {
+    fun addHandler(handlerType: HandlerType, path: String, handler: Handler, roles: Set<RouteRole>) {
         val protectedHandler = if (handlerType.isHttpMethod()) Handler { ctx -> config.inner.accessManager.manage(handler, ctx, roles) } else handler
         matcher.add(HandlerEntry(handlerType, path, config.ignoreTrailingSlashes, protectedHandler, handler))
     }
diff --git a/javalin/src/main/java/io/javalin/websocket/JavalinWsServlet.kt b/javalin/src/main/java/io/javalin/websocket/JavalinWsServlet.kt
index ed5c3c30..f7e862e4 100644
--- a/javalin/src/main/java/io/javalin/websocket/JavalinWsServlet.kt
+++ b/javalin/src/main/java/io/javalin/websocket/JavalinWsServlet.kt
@@ -7,7 +7,7 @@
 package io.javalin.websocket
 
 import io.javalin.core.JavalinConfig
-import io.javalin.core.security.Role
+import io.javalin.core.security.RouteRole
 import io.javalin.core.util.Header
 import io.javalin.http.Context
 import io.javalin.http.JavalinServlet
@@ -57,7 +57,7 @@ class JavalinWsServlet(val config: JavalinConfig, private val httpServlet: Javal
                 pathParamMap = entry.extractPathParams(requestUri)
                 matchedPath = entry.path
             }
-            config.inner.accessManager.manage({ ctx -> ctx.req.setAttribute(upgradeAllowedKey, true) }, upgradeContext, entry.permittedRoles)
+            config.inner.accessManager.manage({ ctx -> ctx.req.setAttribute(upgradeAllowedKey, true) }, upgradeContext, entry.roles)
             if (req.getAttribute(upgradeAllowedKey) != true) throw UnauthorizedResponse() // if set to true, the access manager ran the handler (== valid)
             req.setAttribute(upgradeContextKey, upgradeContext)
 
@@ -75,8 +75,8 @@ class JavalinWsServlet(val config: JavalinConfig, private val httpServlet: Javal
         }
     }
 
-    fun addHandler(handlerType: WsHandlerType, path: String, ws: Consumer<WsConfig>, permittedRoles: Set<Role>) {
-        wsPathMatcher.add(WsEntry(handlerType, path, config.ignoreTrailingSlashes, WsConfig().apply { ws.accept(this) }, permittedRoles))
+    fun addHandler(handlerType: WsHandlerType, path: String, ws: Consumer<WsConfig>, roles: Set<RouteRole>) {
+        wsPathMatcher.add(WsEntry(handlerType, path, config.ignoreTrailingSlashes, WsConfig().apply { ws.accept(this) }, roles))
     }
 }
 
diff --git a/javalin/src/main/java/io/javalin/websocket/WsPathMatcher.kt b/javalin/src/main/java/io/javalin/websocket/WsPathMatcher.kt
index f023508a..2c241966 100644
--- a/javalin/src/main/java/io/javalin/websocket/WsPathMatcher.kt
+++ b/javalin/src/main/java/io/javalin/websocket/WsPathMatcher.kt
@@ -7,10 +7,10 @@
 package io.javalin.websocket
 
 import io.javalin.core.PathParser
-import io.javalin.core.security.Role
+import io.javalin.core.security.RouteRole
 import java.util.*
 
-data class WsEntry(val type: WsHandlerType, val path: String, val ignoreTrailingSlashes: Boolean, val wsConfig: WsConfig, val permittedRoles: Set<Role>) {
+data class WsEntry(val type: WsHandlerType, val path: String, val ignoreTrailingSlashes: Boolean, val wsConfig: WsConfig, val roles: Set<RouteRole>) {
     private val pathParser = PathParser(path, ignoreTrailingSlashes)
     fun matches(path: String) = pathParser.matches(path)
     fun extractPathParams(path: String) = pathParser.extractPathParams(path)


Test patch:
diff --git a/javalin/src/test/java/io/javalin/TestAccessManager.kt b/javalin/src/test/java/io/javalin/TestAccessManager.kt
index 519f739d..2b0607f1 100644
--- a/javalin/src/test/java/io/javalin/TestAccessManager.kt
+++ b/javalin/src/test/java/io/javalin/TestAccessManager.kt
@@ -12,20 +12,19 @@ import io.javalin.TestAccessManager.MyRoles.ROLE_ONE
 import io.javalin.TestAccessManager.MyRoles.ROLE_TWO
 import io.javalin.apibuilder.ApiBuilder.crud
 import io.javalin.apibuilder.ApiBuilder.get
-import io.javalin.core.security.Role
-import io.javalin.core.security.SecurityUtil.roles
+import io.javalin.core.security.RouteRole
 import io.javalin.testing.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
 class TestAccessManager {
 
-    enum class MyRoles : Role { ROLE_ONE, ROLE_TWO, ROLE_THREE }
+    enum class MyRoles : RouteRole { ROLE_ONE, ROLE_TWO, ROLE_THREE }
 
     private val managedApp = Javalin.create { config ->
-        config.accessManager { handler, ctx, permittedRoles ->
+        config.accessManager { handler, ctx, roles ->
             val userRole = ctx.queryParam("role")
-            if (userRole != null && permittedRoles.contains(MyRoles.valueOf(userRole))) {
+            if (userRole != null && roles.contains(MyRoles.valueOf(userRole))) {
                 handler.handle(ctx)
             } else {
                 ctx.status(401).result("Unauthorized")
@@ -35,13 +34,13 @@ class TestAccessManager {
 
     @Test
     fun `default AccessManager throws if roles are present`() = TestUtil.test { app, http ->
-        app.get("/secured", { ctx -> ctx.result("Hello") }, roles(ROLE_ONE))
+        app.get("/secured", { ctx -> ctx.result("Hello") }, ROLE_ONE)
         assertThat(callWithRole(http.origin, "/secured", "ROLE_ONE")).isEqualTo("Internal server error")
     }
 
     @Test
     fun `AccessManager can restrict access for instance`() = TestUtil.test(managedApp) { app, http ->
-        app.get("/secured", { ctx -> ctx.result("Hello") }, roles(ROLE_ONE, ROLE_TWO))
+        app.get("/secured", { ctx -> ctx.result("Hello") }, ROLE_ONE, ROLE_TWO)
         assertThat(callWithRole(http.origin, "/secured", "ROLE_ONE")).isEqualTo("Hello")
         assertThat(callWithRole(http.origin, "/secured", "ROLE_TWO")).isEqualTo("Hello")
         assertThat(callWithRole(http.origin, "/secured", "ROLE_THREE")).isEqualTo("Unauthorized")
@@ -50,7 +49,7 @@ class TestAccessManager {
     @Test
     fun `AccessManager can restrict access for ApiBuilder`() = TestUtil.test(managedApp) { app, http ->
         app.routes {
-            get("/static-secured", { ctx -> ctx.result("Hello") }, roles(ROLE_ONE, ROLE_TWO))
+            get("/static-secured", { ctx -> ctx.result("Hello") }, ROLE_ONE, ROLE_TWO)
         }
         assertThat(callWithRole(http.origin, "/static-secured", "ROLE_ONE")).isEqualTo("Hello")
         assertThat(callWithRole(http.origin, "/static-secured", "ROLE_TWO")).isEqualTo("Hello")
@@ -60,7 +59,7 @@ class TestAccessManager {
     @Test
     fun `AccessManager can restrict access for ApiBuilder crud`() = TestUtil.test(managedApp) { app, http ->
         app.routes {
-            crud("/users/:userId", TestApiBuilder.UserController(), roles(ROLE_ONE, ROLE_TWO))
+            crud("/users/:userId", TestApiBuilder.UserController(), ROLE_ONE, ROLE_TWO)
         }
         assertThat(callWithRole(http.origin, "/users/1", "ROLE_ONE")).isEqualTo("My single user: 1")
         assertThat(callWithRole(http.origin, "/users/2", "ROLE_TWO")).isEqualTo("My single user: 2")
diff --git a/javalin/src/test/java/io/javalin/TestConfiguration.kt b/javalin/src/test/java/io/javalin/TestConfiguration.kt
index de0c12f4..b0436962 100644
--- a/javalin/src/test/java/io/javalin/TestConfiguration.kt
+++ b/javalin/src/test/java/io/javalin/TestConfiguration.kt
@@ -8,7 +8,6 @@ package io.javalin
 
 import io.javalin.core.compression.CompressionStrategy
 import io.javalin.core.compression.Gzip
-import io.javalin.core.security.SecurityUtil.roles
 import io.javalin.core.util.RouteOverviewPlugin
 import io.javalin.http.staticfiles.Location
 import io.javalin.plugin.metrics.MicrometerPlugin
@@ -35,7 +34,7 @@ class TestConfiguration {
             it.defaultContentType = "text/plain"
             it.enableCorsForAllOrigins()
             it.enableDevLogging()
-            it.registerPlugin(RouteOverviewPlugin("/test", roles()))
+            it.registerPlugin(RouteOverviewPlugin("/test"))
             it.enableWebjars()
             it.enforceSsl = true
             it.logIfServerNotStarted = false
@@ -51,7 +50,7 @@ class TestConfiguration {
             }
             it.registerPlugin(MicrometerPlugin())
             // Misc
-            it.accessManager { handler, ctx, permittedRoles -> }
+            it.accessManager { handler, ctx, roles -> }
             it.showJavalinBanner = false
             it.configureServletContextHandler { handler ->
                 handler.addEventListener(object : HttpSessionListener {
diff --git a/javalin/src/test/java/io/javalin/TestCors.kt b/javalin/src/test/java/io/javalin/TestCors.kt
index f9814eaa..a9a100c2 100644
--- a/javalin/src/test/java/io/javalin/TestCors.kt
+++ b/javalin/src/test/java/io/javalin/TestCors.kt
@@ -72,7 +72,7 @@ class TestCors {
             }
         }
         TestUtil.test(accessManagedCorsApp) { app, http ->
-            app.get("/", { ctx -> ctx.result("Hello") }, setOf(TestAccessManager.MyRoles.ROLE_ONE))
+            app.get("/", { ctx -> ctx.result("Hello") }, TestAccessManager.MyRoles.ROLE_ONE)
             assertThat(http.get("/").body).isEqualTo("Unauthorized")
             val response = Unirest.options(http.origin)
                     .header(ACCESS_CONTROL_REQUEST_HEADERS, "123")
diff --git a/javalin/src/test/java/io/javalin/TestJson.kt b/javalin/src/test/java/io/javalin/TestJson.kt
index 72ad0eff..64589ff6 100644
--- a/javalin/src/test/java/io/javalin/TestJson.kt
+++ b/javalin/src/test/java/io/javalin/TestJson.kt
@@ -75,7 +75,7 @@ class TestJson {
         app.get("/hello") { it.body<NonSerializableObject>() }
         assertThat(http.get("/hello").status).isEqualTo(400)
         val response = http.getBody("/hello").replace("\\s".toRegex(), "")
-        assertThat(response).isEqualTo("""{"NonSerializableObject":[{"message":"DESERIALIZATION_FAILED","value":""}]}""")
+        assertThat(response).isEqualTo("""{"NonSerializableObject":[{"message":"DESERIALIZATION_FAILED","args":{},"value":""}]}""")
     }
 
     @Test
diff --git a/javalin/src/test/java/io/javalin/TestValidation.kt b/javalin/src/test/java/io/javalin/TestValidation.kt
index 0d05d7b5..a91875d7 100644
--- a/javalin/src/test/java/io/javalin/TestValidation.kt
+++ b/javalin/src/test/java/io/javalin/TestValidation.kt
@@ -12,10 +12,11 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
 import com.fasterxml.jackson.module.kotlin.readValue
 import io.javalin.core.util.JavalinLogger
 import io.javalin.core.validation.JavalinValidation
+import io.javalin.core.validation.ValidationError
 import io.javalin.core.validation.ValidationException
 import io.javalin.core.validation.Validator
 import io.javalin.core.validation.collectErrors
-import io.javalin.http.BadRequestResponse
+import io.javalin.http.Context
 import io.javalin.http.context.formParam
 import io.javalin.http.context.pathParam
 import io.javalin.http.context.queryParam
@@ -26,6 +27,7 @@ import io.javalin.testing.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.http.HttpStatus
 import org.junit.Test
+import java.text.MessageFormat
 import java.time.Duration
 import java.time.Instant
 
@@ -34,19 +36,19 @@ class TestValidation {
     @Test
     fun `pathParam gives correct error message`() = TestUtil.test { app, http ->
         app.get("/:param") { ctx -> ctx.pathParam<Int>("param").get() }
-        assertThat(http.get("/abc").body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","value":"abc"}]}""")
+        assertThat(http.get("/abc").body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"abc"}]}""")
     }
 
     @Test
     fun `queryParam gives correct error message`() = TestUtil.test { app, http ->
         app.get("/") { ctx -> ctx.queryParam<Int>("param").get() }
-        assertThat(http.get("/?param=abc").body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","value":"abc"}]}""")
+        assertThat(http.get("/?param=abc").body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"abc"}]}""")
     }
 
     @Test
     fun `formParam gives correct error message`() = TestUtil.test { app, http ->
         app.post("/") { ctx -> ctx.formParam<Int>("param").get() }
-        assertThat(http.post("/").body("param=abc").asString().body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","value":"abc"}]}""")
+        assertThat(http.post("/").body("param=abc").asString().body).isEqualTo("""{"param":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"abc"}]}""")
         JavalinLogger.enabled = true
         val log = TestUtil.captureStdOut { http.post("/").body("param=abc").asString().body }
         assertThat(log).contains("Parameter 'param' with value 'abc' is not a valid Integer")
@@ -56,7 +58,7 @@ class TestValidation {
     @Test
     fun `notNullOrEmpty works for Validator`() = TestUtil.test { app, http ->
         app.get("/") { ctx -> ctx.queryParam<String>("my-qp").get() }
-        assertThat(http.get("/").body).isEqualTo("""{"my-qp":[{"message":"NULLCHECK_FAILED","value":null}]}""")
+        assertThat(http.get("/").body).isEqualTo("""{"my-qp":[{"message":"NULLCHECK_FAILED","args":{},"value":null}]}""")
         assertThat(http.get("/").status).isEqualTo(400)
     }
 
@@ -73,8 +75,8 @@ class TestValidation {
             val myInt = ctx.queryParam<Int>("my-qp").get()
             ctx.result((myInt * 2).toString())
         }
-        assertThat(http.get("/int").body).isEqualTo("""{"my-qp":[{"message":"NULLCHECK_FAILED","value":null}]}""")
-        assertThat(http.get("/int?my-qp=abc").body).isEqualTo("""{"my-qp":[{"message":"TYPE_CONVERSION_FAILED","value":"abc"}]}""")
+        assertThat(http.get("/int").body).isEqualTo("""{"my-qp":[{"message":"NULLCHECK_FAILED","args":{},"value":null}]}""")
+        assertThat(http.get("/int?my-qp=abc").body).isEqualTo("""{"my-qp":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"abc"}]}""")
         assertThat(http.get("/int?my-qp=123").body).isEqualTo("246")
     }
 
@@ -83,7 +85,7 @@ class TestValidation {
         app.get("/") { ctx ->
             ctx.queryParam<String>("my-qp").check({ it.length > 5 }, "Length must be more than five").get()
         }
-        assertThat(http.get("/?my-qp=1").body).isEqualTo("""{"my-qp":[{"message":"Length must be more than five","value":"1"}]}""")
+        assertThat(http.get("/?my-qp=1").body).isEqualTo("""{"my-qp":[{"message":"Length must be more than five","args":{},"value":"1"}]}""")
     }
 
     @Test
@@ -92,7 +94,7 @@ class TestValidation {
             val myInt = ctx.queryParam<Int>("my-qp", "788").get()
             ctx.result(myInt.toString())
         }
-        assertThat(http.get("/?my-qp=a").body).isEqualTo("""{"my-qp":[{"message":"TYPE_CONVERSION_FAILED","value":"a"}]}""")
+        assertThat(http.get("/?my-qp=a").body).isEqualTo("""{"my-qp":[{"message":"TYPE_CONVERSION_FAILED","args":{},"value":"a"}]}""")
         assertThat(http.get("/?my-qp=1").body).isEqualTo("1")
         assertThat(http.get("/").body).isEqualTo("788")
     }
@@ -115,7 +117,7 @@ class TestValidation {
             ctx.json(toDate.isAfter(fromDate))
         }
         assertThat(http.get("/instant?from=1262347200000&to=1262347300000").body).isEqualTo("true")
-        assertThat(http.get("/instant?from=1262347200000&to=1262347100000").body).isEqualTo("""{"to":[{"message":"'to' has to be after 'from'","value":1262347100.000000000}]}""")
+        assertThat(http.get("/instant?from=1262347200000&to=1262347100000").body).isEqualTo("""{"to":[{"message":"'to' has to be after 'from'","args":{},"value":1262347100.000000000}]}""")
     }
 
     @Test
@@ -162,10 +164,10 @@ class TestValidation {
             value1 = "Bananas"
         })
 
-        """{"SerializeableObject":[{"message":"DESERIALIZATION_FAILED","value":"not-json"}]}""".let { expected ->
+        """{"SerializeableObject":[{"message":"DESERIALIZATION_FAILED","args":{},"value":"not-json"}]}""".let { expected ->
             assertThat(http.post("/json").body("not-json").asString().body).isEqualTo(expected)
         }
-        """{"SerializeableObject":[{"message":"value1 must be 'Bananas'","value":{"value1":"FirstValue","value2":"SecondValue"}}]}""".let { expected ->
+        """{"SerializeableObject":[{"message":"value1 must be 'Bananas'","args":{},"value":{"value1":"FirstValue","value2":"SecondValue"}}]}""".let { expected ->
             assertThat(http.post("/json").body(invalidJson).asString().body).isEqualTo(expected)
         }
 
@@ -182,9 +184,9 @@ class TestValidation {
                 .get()
         }
         val expected = """{"SerializeableObject":[
-            {"message":"UnnamedFieldCheck1","value":{"value1":"FirstValue","value2":"SecondValue"}},
-            {"message":"UnnamedFieldCheck2","value":{"value1":"First Value","value2":"SecondValue"}}],
-            "named_field":[{"message":"NamedFieldCheck3","value":{"value1":"FirstValue","value2":"SecondValue"}}]}""".replace("\\s".toRegex(), "")
+            {"message":"UnnamedFieldCheck1","args":{},"value":{"value1":"FirstValue","value2":"SecondValue"}},
+            {"message":"UnnamedFieldCheck2","args":{},"value":{"value1":"First Value","value2":"SecondValue"}}],
+            "named_field":[{"message":"NamedFieldCheck3","args":{},"value":{"value1":"FirstValue","value2":"SecondValue"}}]}""".replace("\\s".toRegex(), "")
         val response = http.post("/json").body(JavalinJson.toJson(SerializeableObject())).asString().body
         assertThat(response).isEqualTo(expected)
     }
@@ -238,7 +240,7 @@ class TestValidation {
 
         // Test invalid param
         http.get("/?id=4").apply {
-            assertThat(body).isEqualTo("""{"id":[{"message":"id was not greater than 10","value":4}]}""")
+            assertThat(body).isEqualTo("""{"id":[{"message":"id was not greater than 10","args":{},"value":4}]}""")
             assertThat(status).isEqualTo(400)
         }
 
@@ -300,13 +302,46 @@ class TestValidation {
         // Test invalid param
         http.post("/").body("{\"first_name\":\"Mathilde\"}").asString().apply {
             assertThat(status).isEqualTo(200)
-            assertThat(body).isEqualTo("""{"first_name":[{"message":"Too long","value":{"first_name":"Mathilde"}}]}""")
+            assertThat(body).isEqualTo("""{"first_name":[{"message":"Too long","args":{},"value":{"first_name":"Mathilde"}}]}""")
         }
 
         // Test invalid empty param
         http.post("/").body("{}").asString().apply {
             assertThat(status).isEqualTo(200)
-            assertThat(body).isEqualTo("""{"first_name":[{"message":"This field is mandatory","value":{}}]}""")
+            assertThat(body).isEqualTo("""{"first_name":[{"message":"This field is mandatory","args":{},"value":{}}]}""")
         }
     }
+
+    @Test
+    fun `error args work`() = TestUtil.test { app, http ->
+        app.get("/args") { ctx ->
+            ctx.queryParam<Int>("my-qp")
+                .check({ it > 5 }, ValidationError("OVER_LIMIT", args = mapOf("limit" to 5)))
+                .get()
+        }
+        assertThat(http.get("/args").body).isEqualTo("""{"my-qp":[{"message":"NULLCHECK_FAILED","args":{},"value":null}]}""")
+    }
+
+    @Test
+    fun `localization is easy`() = TestUtil.test { app, http ->
+        app.get("/") { ctx ->
+            ctx.queryParam<Int>("number")
+                .check({ it in 6..9 }, ValidationError("NUMBER_NOT_IN_RANGE", args = mapOf("min" to 6, "max" to 9)))
+                .get()
+        }
+        app.exception(ValidationException::class.java) { e, ctx ->
+            val msgBundle = mapOf(
+                "NUMBER_NOT_IN_RANGE" to mapOf(
+                    "en" to "The value has to at least {0} and at most {1}",
+                    "fr" to "La valeur doit au moins {0} et au plus {1}",
+                )
+            )
+            val error = e.errors["number"]!!.first()
+            val locale = ctx.queryParam("locale")!!
+            val messageTemplate = msgBundle[error.message]!![locale]!!
+            ctx.result(MessageFormat.format(messageTemplate, *error.args.values.toTypedArray()))
+        }
+        assertThat(http.getBody("/?number=20&locale=en")).contains("The value has to at least 6 and at most 9")
+        assertThat(http.getBody("/?number=20&locale=fr")).contains("La valeur doit au moins 6 et au plus 9")
+    }
 }
diff --git a/javalin/src/test/java/io/javalin/TestWebSocket.kt b/javalin/src/test/java/io/javalin/TestWebSocket.kt
index edd63b87..60e134ba 100644
--- a/javalin/src/test/java/io/javalin/TestWebSocket.kt
+++ b/javalin/src/test/java/io/javalin/TestWebSocket.kt
@@ -56,7 +56,7 @@ class TestWebSocket {
     }
 
     fun accessManagedJavalin() = Javalin.create().apply {
-        this._conf.accessManager { handler, ctx, permittedRoles ->
+        this._conf.accessManager { handler, ctx, roles ->
             this.logger().log.add("handling upgrade request ...")
             when {
                 ctx.queryParam("allowed") == "true" -> {
diff --git a/javalin/src/test/java/io/javalin/examples/HelloWorldAuth.java b/javalin/src/test/java/io/javalin/examples/HelloWorldAuth.java
index 8926caef..9f2faf73 100644
--- a/javalin/src/test/java/io/javalin/examples/HelloWorldAuth.java
+++ b/javalin/src/test/java/io/javalin/examples/HelloWorldAuth.java
@@ -8,10 +8,9 @@
 package io.javalin.examples;
 
 import io.javalin.Javalin;
-import io.javalin.core.security.Role;
+import io.javalin.core.security.RouteRole;
 import static io.javalin.apibuilder.ApiBuilder.get;
 import static io.javalin.apibuilder.ApiBuilder.path;
-import static io.javalin.core.security.SecurityUtil.roles;
 import static io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_ONE;
 import static io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_THREE;
 import static io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_TWO;
@@ -20,26 +19,26 @@ public class HelloWorldAuth {
 
     public static void main(String[] args) {
         Javalin.create(config -> {
-            config.accessManager((handler, ctx, permittedRoles) -> {
+            config.accessManager((handler, ctx, routeRoles) -> {
                 String userRole = ctx.queryParam("role");
-                if (userRole != null && permittedRoles.contains(MyRoles.valueOf(userRole))) {
+                if (userRole != null && routeRoles.contains(MyRoles.valueOf(userRole))) {
                     handler.handle(ctx);
                 } else {
                     ctx.status(401).result("Unauthorized");
                 }
             });
         }).routes(() -> {
-            get("/hello", ctx -> ctx.result("Hello World 1"), roles(ROLE_ONE));
+            get("/hello", ctx -> ctx.result("Hello World 1"), ROLE_ONE);
             path("/api", () -> {
-                get("/test", ctx -> ctx.result("Hello World 2"), roles(ROLE_TWO));
-                get("/tast", ctx -> ctx.status(200).result("Hello world 3"), roles(ROLE_THREE));
-                get("/hest", ctx -> ctx.status(200).result("Hello World 4"), roles(ROLE_ONE, ROLE_TWO));
-                get("/hast", ctx -> ctx.status(200).result("Hello World 5").header("test", "tast"), roles(ROLE_ONE, ROLE_THREE));
+                get("/test", ctx -> ctx.result("Hello World 2"), ROLE_TWO);
+                get("/tast", ctx -> ctx.status(200).result("Hello world 3"), ROLE_THREE);
+                get("/hest", ctx -> ctx.status(200).result("Hello World 4"), ROLE_ONE, ROLE_TWO);
+                get("/hast", ctx -> ctx.status(200).result("Hello World 5").header("test", "tast"), ROLE_ONE, ROLE_THREE);
             });
         }).start(7070);
     }
 
-    enum MyRoles implements Role {
+    enum MyRoles implements RouteRole {
         ROLE_ONE, ROLE_TWO, ROLE_THREE
     }
 
diff --git a/javalin/src/test/java/io/javalin/routeoverview/VisualTest.java b/javalin/src/test/java/io/javalin/routeoverview/VisualTest.java
index b1e17224..c0a560be 100644
--- a/javalin/src/test/java/io/javalin/routeoverview/VisualTest.java
+++ b/javalin/src/test/java/io/javalin/routeoverview/VisualTest.java
@@ -21,7 +21,6 @@ import static io.javalin.apibuilder.ApiBuilder.get;
 import static io.javalin.apibuilder.ApiBuilder.patch;
 import static io.javalin.apibuilder.ApiBuilder.path;
 import static io.javalin.apibuilder.ApiBuilder.post;
-import static io.javalin.core.security.SecurityUtil.roles;
 
 public class VisualTest {
 
@@ -38,26 +37,26 @@ public class VisualTest {
         app.get("/", ctx -> ctx.redirect("/context-path/route-overview"))
             .get("/just-some-path", new HandlerImplementation())
             .post("/test/:hmm/", VisualTest::methodReference)
-            .put("/user/*", ctx -> ctx.result(""), roles(ROLE_ONE))
-            .get("/nonsense-paths/:test", VisualTest.lambdaField, roles(ROLE_ONE, ROLE_THREE))
-            .delete("/just-words", VisualTest::methodReference, roles(ROLE_ONE, ROLE_TWO))
+            .put("/user/*", ctx -> ctx.result(""), ROLE_ONE)
+            .get("/nonsense-paths/:test", VisualTest.lambdaField, ROLE_ONE, ROLE_THREE)
+            .delete("/just-words", VisualTest::methodReference, ROLE_ONE, ROLE_TWO)
             .before("*", VisualTest.lambdaField)
             .after("*", VisualTest.lambdaField)
             .head("/check/the/head", VisualTest::methodReference)
             .get("/:path1/:path2", VisualTest.lambdaField)
-            .post("/user/create", VisualTest::methodReference, roles(ROLE_ONE, ROLE_TWO))
+            .post("/user/create", VisualTest::methodReference, ROLE_ONE, ROLE_TWO)
             .put("/user/:user-id", VisualTest.lambdaField)
-            .patch("/patchy-mcpatchface", new ImplementingClass(), roles(ROLE_ONE, ROLE_TWO))
+            .patch("/patchy-mcpatchface", new ImplementingClass(), ROLE_ONE, ROLE_TWO)
             .delete("/users/:user-id", new HandlerImplementation())
             .options("/what/:are/*/my-options", new HandlerImplementation())
-            .options("/what/:are/*/my-options2", new HandlerImplementation(), roles(ROLE_ONE, ROLE_TWO))
+            .options("/what/:are/*/my-options2", new HandlerImplementation(), ROLE_ONE, ROLE_TWO)
             .wsBefore(VisualTest::wsMethodRef)
             .ws("/websocket", VisualTest::wsMethodRef)
             .wsAfter("/my-path", VisualTest::wsMethodRef)
             .addHandler(HandlerType.CONNECT, "/test", VisualTest.lambdaField)
             .addHandler(HandlerType.TRACE, "/tracer", new HandlerImplementation())
-            .addHandler(HandlerType.CONNECT, "/test2", VisualTest.lambdaField, roles(ROLE_ONE, ROLE_TWO))
-            .addHandler(HandlerType.TRACE, "/tracer2", new HandlerImplementation(), roles(ROLE_ONE, ROLE_TWO))
+            .addHandler(HandlerType.CONNECT, "/test2", VisualTest.lambdaField, ROLE_ONE, ROLE_TWO)
+            .addHandler(HandlerType.TRACE, "/tracer2", new HandlerImplementation(), ROLE_ONE, ROLE_TWO)
             .sse("/sse", sse -> {
             });
         app.routes(() -> {
diff --git a/javalin/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt b/javalin/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
index 3a64b9d3..fac3b478 100644
--- a/javalin/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
+++ b/javalin/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
@@ -9,15 +9,14 @@ package io.javalin.examples
 import io.javalin.Javalin
 import io.javalin.apibuilder.ApiBuilder.get
 import io.javalin.apibuilder.ApiBuilder.path
-import io.javalin.core.security.Role
-import io.javalin.core.security.SecurityUtil.roles
+import io.javalin.core.security.RouteRole
 import io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_ONE
 import io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_THREE
 import io.javalin.examples.HelloWorldAuth.MyRoles.ROLE_TWO
 import io.javalin.http.Context
 import io.javalin.http.Handler
 
-enum class MyRoles : Role {
+enum class MyRoles : RouteRole {
     ROLE_ONE, ROLE_TWO, ROLE_THREE
 }
 
@@ -26,20 +25,20 @@ fun main() {
     val app = Javalin.create { it.accessManager(::accessManager) }.start(7070)
 
     app.routes {
-        get("/hello", { ctx -> ctx.result("Hello World 1") }, roles(ROLE_ONE))
+        get("/hello", { ctx -> ctx.result("Hello World 1") }, ROLE_ONE)
         path("/api") {
-            get("/test", { ctx -> ctx.result("Hello World 2") }, roles(ROLE_TWO))
-            get("/tast", { ctx -> ctx.status(200).result("Hello world 3") }, roles(ROLE_THREE))
-            get("/hest", { ctx -> ctx.status(200).result("Hello World 4") }, roles(ROLE_ONE, ROLE_TWO))
-            get("/hast", { ctx -> ctx.status(200).result("Hello World 5").header("test", "tast") }, roles(ROLE_ONE, ROLE_THREE))
+            get("/test", { ctx -> ctx.result("Hello World 2") }, ROLE_TWO)
+            get("/tast", { ctx -> ctx.status(200).result("Hello world 3") }, ROLE_THREE)
+            get("/hest", { ctx -> ctx.status(200).result("Hello World 4") }, ROLE_ONE, ROLE_TWO)
+            get("/hast", { ctx -> ctx.status(200).result("Hello World 5").header("test", "tast") }, ROLE_ONE, ROLE_THREE)
         }
     }
 
 }
 
-private fun accessManager(handler: Handler, ctx: Context, permittedRoles: MutableSet<Role>) {
+private fun accessManager(handler: Handler, ctx: Context, routeRoles: Set<RouteRole>) {
     val userRole = ctx.queryParam("role")
-    if (userRole != null && permittedRoles.contains(MyRoles.valueOf(userRole))) {
+    if (userRole != null && routeRoles.contains(MyRoles.valueOf(userRole))) {
         handler.handle(ctx)
     } else {
         ctx.status(401).result("Unauthorized")