Repository: tipsy/javalin
Pull request #553 (2019-04-15 20:35:11)

[core] Move configure into create
- Also make faux private fields public to highlight misusage

tipsy: Maybe copy the config to a data-class in the `create` method?

Patch:
diff --git a/src/main/java/io/javalin/Javalin.java b/src/main/java/io/javalin/Javalin.java
index 3597fbed..b12a6f60 100644
--- a/src/main/java/io/javalin/Javalin.java
+++ b/src/main/java/io/javalin/Javalin.java
@@ -18,11 +18,12 @@ import io.javalin.core.JavalinEvent;
 import io.javalin.core.JavalinServer;
 import io.javalin.core.JavalinServlet;
 import io.javalin.core.JavalinWsServlet;
-import io.javalin.core.util.CorsUtil;
-import io.javalin.core.util.LogUtil;
+import io.javalin.core.util.CorsBeforeHandler;
+import io.javalin.core.util.CorsOptionsHandler;
 import io.javalin.core.util.RouteOverviewRenderer;
 import io.javalin.core.util.Util;
 import io.javalin.security.AccessManager;
+import io.javalin.security.CoreRoles;
 import io.javalin.security.Role;
 import io.javalin.security.SecurityUtil;
 import io.javalin.serversentevent.SseClient;
@@ -36,13 +37,12 @@ import java.util.function.Consumer;
 import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import static io.javalin.security.SecurityUtil.roles;
 
 public class Javalin {
 
     public static Logger log = LoggerFactory.getLogger(Javalin.class);
 
-    protected boolean showJavalinBanner = true;
-
     protected Map<Class<?>, Object> appAttributes = new HashMap<>();
 
     protected JavalinConfig config = new JavalinConfig();
@@ -55,20 +55,41 @@ public class Javalin {
     public EventAttacher on = eventManager.getEventAttacher();
 
     protected Javalin() {
-        Util.logJavalinBanner(showJavalinBanner);
-        Util.logWarningIfNotStartedAfterOneSecond(server);
     }
 
     /**
-     * Creates an instance of the application for further configuration.
+     * Creates a new instance without any custom configuration.
+     *
+     * @see Javalin#create(Consumer)
+     */
+    public static Javalin create() {
+        return create(c -> { // use defaults
+        });
+    }
+
+    /**
+     * Creates a new instance with the user provided configuration.
      * The server does not run until {@link Javalin#start()} is called.
      *
-     * @return instance of application for configuration.
+     * @return application instance.
      * @see Javalin#start()
      * @see Javalin#start(int)
      */
-    public static Javalin create() {
-        return new Javalin();
+    public static Javalin create(Consumer<JavalinConfig> config) {
+        Javalin app = new Javalin();
+        config.accept(app.config); // apply user config
+        if (app.config.inner.routeOverview != null) {
+            app.get(app.config.inner.routeOverview.getPath(), new RouteOverviewRenderer(app), app.config.inner.routeOverview.getRoles());
+        }
+        if (!app.config.inner.corsOrigins.isEmpty()) {
+            app.before(new CorsBeforeHandler(app.config.inner.corsOrigins));
+            app.options("*", new CorsOptionsHandler(), roles(CoreRoles.NO_WRAP));
+        }
+        if (app.config.enforceSsl) {
+            app.before(SecurityUtil::sslRedirect);
+        }
+        Util.logWarningIfNotStartedAfterOneSecond(app.server);
+        return app;
     }
 
     /**
@@ -91,6 +112,7 @@ public class Javalin {
      * @see Javalin#create()
      */
     public Javalin start() {
+        Util.logJavalinBanner(this.config.showJavalinBanner);
         long startupTimer = System.currentTimeMillis();
         if (server.getStarted()) {
             throw new IllegalStateException("Cannot call start() again on a started server.");
@@ -133,15 +155,6 @@ public class Javalin {
         return this;
     }
 
-    /**
-     * Configure instance to not show banner in logs.
-     * The method must be called before {@link Javalin#start()}.
-     */
-    public Javalin disableStartupBanner() {
-        showJavalinBanner = false;
-        return this;
-    }
-
     /**
      * Get which port instance is running on
      * Mostly useful if you start the instance with port(0) (random port)
@@ -150,46 +163,6 @@ public class Javalin {
         return server.getServerPort();
     }
 
-    /**
-     * Set the server port
-     * Mostly useful if you start the instance with port(0) (random port)
-     */
-    public Javalin port(int port) {
-        server.setServerPort(port);
-        return this;
-    }
-
-    /**
-     * Configure instance to log debug information for each request.
-     * The method must be called before {@link Javalin#start()}.
-     */
-    public Javalin enableDevLogging() {
-        config.requestLogger(LogUtil::requestDevLogger);
-        config.wsLogger(LogUtil::wsDevLogger);
-        return this;
-    }
-
-    /**
-     * Configure instance to display a visual overview of all its mapped routes
-     * on the specified path.
-     * The method must be called before {@link Javalin#start()}.
-     */
-    public Javalin enableRouteOverview(@NotNull String path) {
-        return enableRouteOverview(path, new HashSet<>());
-    }
-
-    /**
-     * Configure instance to display a visual overview of all its mapped routes
-     * on the specified path with the specified roles
-     * The method must be called before {@link Javalin#start()}.
-     */
-    public Javalin enableRouteOverview(@NotNull String path, @NotNull Set<Role> permittedRoles) {
-        if (servlet.getMatcher().hasEntries()) {
-            throw new IllegalStateException("The route-overview listens for 'onHandlerAdded' events. It must be enabled before adding handlers.");
-        }
-        return get(path, new RouteOverviewRenderer(this), permittedRoles);
-    }
-
     /**
      * Registers an attribute on the instance.
      * Instance is available on the {@link Context} through {@link Context#appAttribute}.
@@ -554,20 +527,4 @@ public class Javalin {
         return this;
     }
 
-    // ********************************************************************************************
-    // Configuration
-    // ********************************************************************************************
-
-    public Javalin configure(Consumer<JavalinConfig> config) {
-        if (this.server.getStarted()) throw new IllegalStateException("Cannot call 'configure()' after server start");
-        config.accept(this.config); // apply user config
-        if (!this.config.corsOrigins.isEmpty()) {
-            CorsUtil.enableCorsForOrigin(this.servlet, this.config.corsOrigins);
-        }
-        if (this.config.enforceSsl) {
-            this.before(SecurityUtil::sslRedirect);
-        }
-        return this;
-    }
-
 }
diff --git a/src/main/java/io/javalin/core/JavalinConfig.java b/src/main/java/io/javalin/core/JavalinConfig.java
index 45faa8d6..29dcc345 100644
--- a/src/main/java/io/javalin/core/JavalinConfig.java
+++ b/src/main/java/io/javalin/core/JavalinConfig.java
@@ -7,8 +7,11 @@
 package io.javalin.core;
 
 import io.javalin.RequestLogger;
+import io.javalin.core.util.LogUtil;
+import io.javalin.core.util.RouteOverviewConfig;
 import io.javalin.core.util.SinglePageHandler;
 import io.javalin.security.AccessManager;
+import io.javalin.security.Role;
 import io.javalin.security.SecurityUtil;
 import io.javalin.staticfiles.JettyResourceHandler;
 import io.javalin.staticfiles.Location;
@@ -17,92 +20,99 @@ import io.javalin.staticfiles.StaticFileConfig;
 import io.javalin.websocket.WsHandler;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.session.SessionHandler;
 import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 // @formatter:off
 public class JavalinConfig {
 
-    // ********************************************************************************************
-    // HTTP Servlet
-    // ********************************************************************************************
-
     public boolean dynamicGzip = true;
     public boolean autogenerateEtags = false;
     public boolean prefer405over404 = false;
     public boolean enforceSsl = false;
-    public String defaultContentType = "text/plain";
-    public String contextPath = "/";
-    public Long requestCacheSize = 4096L;
-    public Long asyncRequestTimeout = 0L;
-    public List<String> corsOrigins = new ArrayList<>();
-    RequestLogger requestLogger;
-    ResourceHandler resourceHandler;
-    public AccessManager accessManager = SecurityUtil::noopAccessManager;
-    SinglePageHandler singlePageHandler = new SinglePageHandler();
-    SessionHandler sessionHandler;
+    public boolean showJavalinBanner = true;
+    @NotNull public String defaultContentType = "text/plain";
+    @NotNull public String contextPath = "/";
+    @NotNull public Long requestCacheSize = 4096L;
+    @NotNull public Long asyncRequestTimeout = 0L;
+    @NotNull public String wsContextPath ="/";
+    @NotNull public Inner inner = new Inner();
+
+    // it's not bad to access this, the main reason it's hidden
+    // is to provide a cleaner API with dedicated setters
+    public class Inner {
+        @NotNull public List<String> corsOrigins = new ArrayList<>();
+        @Nullable public RouteOverviewConfig routeOverview;
+        @Nullable public RequestLogger requestLogger;
+        @Nullable public ResourceHandler resourceHandler;
+        @NotNull public AccessManager accessManager = SecurityUtil::noopAccessManager;
+        @NotNull public SinglePageHandler singlePageHandler = new SinglePageHandler();
+        @Nullable public SessionHandler sessionHandler;
+        @Nullable public Consumer<WebSocketServletFactory> wsFactoryConfig;
+        @Nullable public WsHandler wsLogger;
+        @Nullable public Server server;
+    }
+
+     public void enableDevLogging() {
+        requestLogger(LogUtil::requestDevLogger);
+        wsLogger(LogUtil::wsDevLogger);
+    }
 
     public void enableWebjars() { addStaticFiles("/webjars", Location.CLASSPATH); }
     public void addStaticFiles(@NotNull String classpathPath) { addStaticFiles(classpathPath, Location.CLASSPATH); }
     public void addStaticFiles(@NotNull String path, @NotNull Location location) {
-        if (resourceHandler == null) this.resourceHandler = new JettyResourceHandler();
-        this.resourceHandler.addStaticFileConfig(new StaticFileConfig(path, location));
+        if (inner.resourceHandler == null) inner.resourceHandler = new JettyResourceHandler();
+        inner.resourceHandler.addStaticFileConfig(new StaticFileConfig(path, location));
     }
 
     public void addSinglePageRoot(@NotNull String path, @NotNull String filePath) { addSinglePageRoot(path, filePath, Location.CLASSPATH); }
     public void addSinglePageRoot(@NotNull String path, @NotNull String filePath, @NotNull Location location) {
-        this.singlePageHandler.add(path, filePath, location);
+        inner.singlePageHandler.add(path, filePath, location);
     }
 
     public void enableCorsForAllOrigins() { enableCorsForOrigin("*"); }
     public void enableCorsForOrigin(@NotNull String... origins) {
         if (origins.length == 0) throw new IllegalArgumentException("Origins cannot be empty.");
-        this.corsOrigins = Arrays.asList(origins);
+        inner.corsOrigins = Arrays.asList(origins);
+    }
+
+    public void enableRouteOverview(@NotNull String path) { enableRouteOverview(path, new HashSet<>()); }
+    public void enableRouteOverview(@NotNull String path, @NotNull Set<Role> permittedRoles) {
+        inner.routeOverview = new RouteOverviewConfig(path, permittedRoles);
     }
 
     public void accessManager(@NotNull AccessManager accessManager) {
-        this.accessManager = accessManager;
+        inner.accessManager = accessManager;
     }
 
     public void requestLogger(@NotNull RequestLogger requestLogger) {
-        this.requestLogger = requestLogger;
+        inner.requestLogger = requestLogger;
     }
 
     public void sessionHandler(@NotNull Supplier<SessionHandler> sessionHandlerSupplier) {
-        this.sessionHandler = JettyUtil.getSessionHandler(sessionHandlerSupplier);
+        inner.sessionHandler = JettyUtil.getSessionHandler(sessionHandlerSupplier);
     }
 
-    // ********************************************************************************************
-    // WebSocket Servlet
-    // ********************************************************************************************
-
-    Consumer<WebSocketServletFactory> wsFactoryConfig;
-    public String wsContextPath ="/";
-    public WsHandler wsLogger;
-
     public void wsFactoryConfig(@NotNull Consumer<WebSocketServletFactory> wsFactoryConfig) {
-        this.wsFactoryConfig = wsFactoryConfig;
+        inner.wsFactoryConfig = wsFactoryConfig;
     }
 
     public void wsLogger(@NotNull Consumer<WsHandler> ws) {
         WsHandler logger = new WsHandler();
         ws.accept(logger);
-        this.wsLogger = logger;
+        inner.wsLogger = logger;
     }
 
-    // ********************************************************************************************
-    // Server
-    // ********************************************************************************************
-
-    Server server;
-
     public void server(Supplier<Server> server) {
-        this.server = server.get();
+        inner.server = server.get();
     }
 
 }
diff --git a/src/main/java/io/javalin/core/JavalinServer.kt b/src/main/java/io/javalin/core/JavalinServer.kt
index a15efddf..883750c9 100644
--- a/src/main/java/io/javalin/core/JavalinServer.kt
+++ b/src/main/java/io/javalin/core/JavalinServer.kt
@@ -32,17 +32,17 @@ class JavalinServer(val config: JavalinConfig) {
         disableJettyLogger()
     }
 
-    fun server() = config.server
+    fun server() = config.inner.server!!
     var started = false
 
     @Throws(BindException::class)
     fun start(javalinServlet: JavalinServlet, javalinWsServlet: JavalinWsServlet) {
 
-        config.server = config.server ?: defaultServer()
-        javalinServlet.config.sessionHandler = javalinServlet.config.sessionHandler ?: defaultSessionHandler()
+        config.inner.server = config.inner.server ?: defaultServer()
+        config.inner.sessionHandler = config.inner.sessionHandler ?: defaultSessionHandler()
         val nullParent = null // javalin handlers are orphans
 
-        val httpHandler = object : ServletContextHandler(nullParent, javalinServlet.config.contextPath, SESSIONS) {
+        val httpHandler = object : ServletContextHandler(nullParent, config.contextPath, SESSIONS) {
             override fun doHandle(target: String, jettyRequest: Request, request: HttpServletRequest, response: HttpServletResponse) {
                 if (request.isWebSocket()) return // don't touch websocket requests
                 try {
@@ -55,30 +55,30 @@ class JavalinServer(val config: JavalinConfig) {
                 }
                 jettyRequest.isHandled = true
             }
-        }.apply { this.sessionHandler = javalinServlet.config.sessionHandler }
+        }.apply { this.sessionHandler = config.inner.sessionHandler }
 
         val webSocketHandler = ServletContextHandler(nullParent, javalinWsServlet.config.wsContextPath, SESSIONS).apply {
             addServlet(ServletHolder(javalinWsServlet), "/*")
         }
 
-        config.server.apply {
+        server().apply {
             handler = attachJavalinHandlers(server.handler, HandlerList(httpHandler, webSocketHandler))
             connectors = connectors.takeIf { it.isNotEmpty() } ?: arrayOf(ServerConnector(server).apply {
                 this.port = serverPort
             })
         }.start()
 
-        config.server.connectors.filterIsInstance<ServerConnector>().forEach {
-            Javalin.log.info("Listening on ${it.protocol}://${it.host ?: "localhost"}:${it.localPort}${javalinServlet.config.contextPath}")
+        server().connectors.filterIsInstance<ServerConnector>().forEach {
+            Javalin.log.info("Listening on ${it.protocol}://${it.host ?: "localhost"}:${it.localPort}${config.contextPath}")
         }
 
-        config.server.connectors.filter { it !is ServerConnector }.forEach {
+        server().connectors.filter { it !is ServerConnector }.forEach {
             Javalin.log.info("Binding to: $it")
         }
 
         reEnableJettyLogger()
         started = true
-        serverPort = (config.server.connectors[0] as? ServerConnector)?.localPort ?: -1
+        serverPort = (server().connectors[0] as? ServerConnector)?.localPort ?: -1
     }
 
     private fun disableJettyLogger() = org.eclipse.jetty.util.log.Log.setLog(NoopLogger()) // disable logger before server creation
diff --git a/src/main/java/io/javalin/core/JavalinServlet.kt b/src/main/java/io/javalin/core/JavalinServlet.kt
index 6b42ad28..af0f0210 100644
--- a/src/main/java/io/javalin/core/JavalinServlet.kt
+++ b/src/main/java/io/javalin/core/JavalinServlet.kt
@@ -45,8 +45,8 @@ class JavalinServlet(private val appAttributes: Map<Class<*>, Any>, val config:
                 return@tryWithExceptionMapper // return 200, there is a get handler
             }
             if (type == HandlerType.HEAD || type == HandlerType.GET) { // let Jetty check for static resources
-                if (config.resourceHandler?.handle(req, res) == true) return@tryWithExceptionMapper
-                if (config.singlePageHandler.handle(ctx)) return@tryWithExceptionMapper
+                if (config.inner.resourceHandler?.handle(req, res) == true) return@tryWithExceptionMapper
+                if (config.inner.singlePageHandler.handle(ctx)) return@tryWithExceptionMapper
             }
             val availableHandlerTypes = MethodNotAllowedUtil.findAvailableHttpHandlerTypes(matcher, requestUri)
             if (config.prefer405over404 && availableHandlerTypes.isNotEmpty()) {
@@ -96,7 +96,7 @@ class JavalinServlet(private val appAttributes: Map<Class<*>, Any>, val config:
             tryErrorHandlers()
             tryAfterHandlers()
             writeResult(res)
-            config.requestLogger?.handle(ctx, LogUtil.executionTimeMs(ctx))
+            config.inner.requestLogger?.handle(ctx, LogUtil.executionTimeMs(ctx))
             return // sync lifecycle complete
         } else { // finish request asynchronously
             val asyncContext = req.startAsync().apply { timeout = config.asyncRequestTimeout }
@@ -113,7 +113,7 @@ class JavalinServlet(private val appAttributes: Map<Class<*>, Any>, val config:
                 tryErrorHandlers()
                 tryAfterHandlers()
                 writeResult(asyncContext.response as HttpServletResponse)
-                config.requestLogger?.handle(ctx, LogUtil.executionTimeMs(ctx))
+                config.inner.requestLogger?.handle(ctx, LogUtil.executionTimeMs(ctx))
                 asyncContext.complete() // async lifecycle complete
             }
         }
@@ -127,7 +127,7 @@ class JavalinServlet(private val appAttributes: Map<Class<*>, Any>, val config:
 
     fun addHandler(handlerType: HandlerType, path: String, handler: Handler, roles: Set<Role>) {
         val shouldWrap = handlerType.isHttpMethod() && !roles.contains(CoreRoles.NO_WRAP)
-        val protectedHandler = if (shouldWrap) Handler { ctx -> config.accessManager.manage(handler, ctx, roles) } else handler
+        val protectedHandler = if (shouldWrap) Handler { ctx -> config.inner.accessManager.manage(handler, ctx, roles) } else handler
         matcher.add(HandlerEntry(handlerType, path, protectedHandler, handler))
     }
 }
diff --git a/src/main/java/io/javalin/core/JavalinWsServlet.kt b/src/main/java/io/javalin/core/JavalinWsServlet.kt
index bafdee02..1febe374 100644
--- a/src/main/java/io/javalin/core/JavalinWsServlet.kt
+++ b/src/main/java/io/javalin/core/JavalinWsServlet.kt
@@ -28,7 +28,7 @@ class JavalinWsServlet(val config: JavalinConfig) : WebSocketServlet() {
     var wsPathMatcher = WsPathMatcher(config)
 
     override fun configure(factory: WebSocketServletFactory) {
-        config.wsFactoryConfig?.accept(factory)
+        config.inner.wsFactoryConfig?.accept(factory)
         factory.creator = WebSocketCreator { req, res ->
             val preUpgradeContext = req.httpServletRequest.getAttribute("javalin-ws-upgrade-context") as Context
             req.httpServletRequest.setAttribute("javalin-ws-upgrade-context", ContextUtil.changeBaseRequest(preUpgradeContext, req.httpServletRequest))
@@ -45,7 +45,7 @@ class JavalinWsServlet(val config: JavalinConfig) : WebSocketServlet() {
                     pathParamMap = entry.extractPathParams(requestUri)
                     matchedPath = entry.path
                 }
-                config.accessManager.manage({ ctx -> ctx.req.setAttribute("javalin-ws-upgrade-allowed", "true") }, upgradeContext, entry.permittedRoles)
+                config.inner.accessManager.manage({ ctx -> ctx.req.setAttribute("javalin-ws-upgrade-allowed", "true") }, upgradeContext, entry.permittedRoles)
                 if (req.getAttribute("javalin-ws-upgrade-allowed") != "true") throw UnauthorizedResponse() // if set to true, the access manager ran the handler (== valid)
                 req.setAttribute("javalin-ws-upgrade-context", upgradeContext)
                 super.service(req, res)
diff --git a/src/main/java/io/javalin/core/util/CorsUtil.kt b/src/main/java/io/javalin/core/util/CorsUtil.kt
index bfa922d4..042c0571 100644
--- a/src/main/java/io/javalin/core/util/CorsUtil.kt
+++ b/src/main/java/io/javalin/core/util/CorsUtil.kt
@@ -8,18 +8,6 @@ package io.javalin.core.util
 
 import io.javalin.Context
 import io.javalin.Handler
-import io.javalin.core.HandlerType
-import io.javalin.core.JavalinServlet
-import io.javalin.security.CoreRoles
-
-object CorsUtil {
-    @JvmStatic
-    fun enableCorsForOrigin(servlet: JavalinServlet, origins: List<String>) {
-        servlet.addHandler(HandlerType.BEFORE, "*", CorsBeforeHandler(origins), setOf())
-        servlet.addHandler(HandlerType.OPTIONS, "*", CorsOptionsHandler(), setOf(CoreRoles.NO_WRAP))
-    }
-}
-
 
 class CorsBeforeHandler(private val origins: List<String>) : Handler {
     override fun handle(ctx: Context) {
diff --git a/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt b/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
index f18a7f86..6d57effa 100644
--- a/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
+++ b/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
@@ -10,6 +10,9 @@ import io.javalin.Context
 import io.javalin.Handler
 import io.javalin.Javalin
 import io.javalin.core.HandlerMetaInfo
+import io.javalin.security.Role
+
+data class RouteOverviewConfig(val path: String, val roles: Set<Role>)
 
 class RouteOverviewRenderer(val app: Javalin) : Handler {
 
diff --git a/src/main/java/io/javalin/security/SecurityUtil.kt b/src/main/java/io/javalin/security/SecurityUtil.kt
index 669aef84..4753b337 100644
--- a/src/main/java/io/javalin/security/SecurityUtil.kt
+++ b/src/main/java/io/javalin/security/SecurityUtil.kt
@@ -17,7 +17,7 @@ object SecurityUtil {
     @JvmStatic
     fun noopAccessManager(handler: Handler, ctx: Context, permittedRoles: Set<Role>) {
         if (permittedRoles.isNotEmpty()) {
-            throw IllegalStateException("No access manager configured. Add an access manager using 'app.configure(c -> c.accessManager(...))'.")
+            throw IllegalStateException("No access manager configured. Add an access manager using 'Javalin.create(c -> c.accessManager(...))'.")
         }
         handler.handle(ctx)
     }
diff --git a/src/main/java/io/javalin/websocket/WsPathMatcher.kt b/src/main/java/io/javalin/websocket/WsPathMatcher.kt
index 2c98cd31..b34ebfeb 100644
--- a/src/main/java/io/javalin/websocket/WsPathMatcher.kt
+++ b/src/main/java/io/javalin/websocket/WsPathMatcher.kt
@@ -36,7 +36,7 @@ class WsPathMatcher(val config: JavalinConfig) {
         val entry = cacheSessionAndFindEntry(session)
         val ctx = WsConnectContext(sessionIds[session]!!, session)
         entry.handler.connectHandler?.handleConnect(ctx)
-        config.wsLogger?.connectHandler?.handleConnect(ctx)
+        config.inner.wsLogger?.connectHandler?.handleConnect(ctx)
     }
 
     @OnWebSocketMessage
@@ -44,7 +44,7 @@ class WsPathMatcher(val config: JavalinConfig) {
         val entry = cacheSessionAndFindEntry(session)
         val ctx = WsMessageContext(sessionIds[session]!!, session, message)
         entry.handler.messageHandler?.handleMessage(ctx)
-        config.wsLogger?.messageHandler?.handleMessage(ctx)
+        config.inner.wsLogger?.messageHandler?.handleMessage(ctx)
     }
 
     @OnWebSocketMessage
@@ -52,7 +52,7 @@ class WsPathMatcher(val config: JavalinConfig) {
         val entry = cacheSessionAndFindEntry(session)
         val ctx = WsBinaryMessageContext(sessionIds[session]!!, session, buffer.toTypedArray(), offset, length)
         entry.handler.binaryMessageHandler?.handleBinaryMessage(ctx)
-        config.wsLogger?.binaryMessageHandler?.handleBinaryMessage(ctx)
+        config.inner.wsLogger?.binaryMessageHandler?.handleBinaryMessage(ctx)
     }
 
     @OnWebSocketError
@@ -60,7 +60,7 @@ class WsPathMatcher(val config: JavalinConfig) {
         val entry = cacheSessionAndFindEntry(session)
         val ctx = WsErrorContext(sessionIds[session]!!, session, throwable)
         entry.handler.errorHandler?.handleError(ctx)
-        config.wsLogger?.errorHandler?.handleError(ctx)
+        config.inner.wsLogger?.errorHandler?.handleError(ctx)
     }
 
     @OnWebSocketClose
@@ -68,7 +68,7 @@ class WsPathMatcher(val config: JavalinConfig) {
         val entry = cacheSessionAndFindEntry(session)
         val ctx = WsCloseContext(sessionIds[session]!!, session, statusCode, reason)
         entry.handler.closeHandler?.handleClose(ctx)
-        config.wsLogger?.closeHandler?.handleClose(ctx)
+        config.inner.wsLogger?.closeHandler?.handleClose(ctx)
         sessionIds.remove(session)
     }
 


Test patch:
diff --git a/src/test/java/io/javalin/TestAccessManager.kt b/src/test/java/io/javalin/TestAccessManager.kt
index 6248e9bf..1cfa5eeb 100644
--- a/src/test/java/io/javalin/TestAccessManager.kt
+++ b/src/test/java/io/javalin/TestAccessManager.kt
@@ -13,7 +13,6 @@ import io.javalin.TestAccessManager.MyRoles.ROLE_TWO
 import io.javalin.apibuilder.ApiBuilder.get
 import io.javalin.security.Role
 import io.javalin.security.SecurityUtil.roles
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
@@ -21,7 +20,7 @@ class TestAccessManager {
 
     enum class MyRoles : Role { ROLE_ONE, ROLE_TWO, ROLE_THREE }
 
-    private val managedApp = Javalin.create().configure { config ->
+    private val managedApp = Javalin.create { config ->
         config.accessManager { handler, ctx, permittedRoles ->
             val userRole = ctx.queryParam("role")
             if (userRole != null && permittedRoles.contains(MyRoles.valueOf(userRole))) {
diff --git a/src/test/java/io/javalin/TestApiBuilder.kt b/src/test/java/io/javalin/TestApiBuilder.kt
index f4cd72cc..251dd6dc 100644
--- a/src/test/java/io/javalin/TestApiBuilder.kt
+++ b/src/test/java/io/javalin/TestApiBuilder.kt
@@ -12,8 +12,7 @@ import com.mashape.unirest.http.Unirest
 import io.javalin.apibuilder.ApiBuilder
 import io.javalin.apibuilder.ApiBuilder.*
 import io.javalin.apibuilder.CrudHandler
-import io.javalin.util.TestUtil
-import io.javalin.util.TestUtil.okHandler
+import io.javalin.TestUtil.okHandler
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestAppAttributes.kt b/src/test/java/io/javalin/TestAppAttributes.kt
index 42bd721b..0f1cbe99 100644
--- a/src/test/java/io/javalin/TestAppAttributes.kt
+++ b/src/test/java/io/javalin/TestAppAttributes.kt
@@ -8,7 +8,6 @@ package io.javalin
 
 import com.google.gson.GsonBuilder
 import io.javalin.misc.SerializeableObject
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestBodyReading.kt b/src/test/java/io/javalin/TestBodyReading.kt
index 1552e3df..c8b12574 100644
--- a/src/test/java/io/javalin/TestBodyReading.kt
+++ b/src/test/java/io/javalin/TestBodyReading.kt
@@ -7,7 +7,6 @@
 
 package io.javalin
 
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.io.ByteArrayInputStream
diff --git a/src/test/java/io/javalin/TestConfiguration.kt b/src/test/java/io/javalin/TestConfiguration.kt
index 5477bc61..b5c04377 100644
--- a/src/test/java/io/javalin/TestConfiguration.kt
+++ b/src/test/java/io/javalin/TestConfiguration.kt
@@ -7,22 +7,15 @@
 package io.javalin
 
 import io.javalin.staticfiles.Location
-import io.javalin.util.TestUtil
 import org.eclipse.jetty.server.Server
 import org.eclipse.jetty.server.session.SessionHandler
 import org.junit.Test
 
 class TestConfiguration {
 
-    @Test(expected = IllegalStateException::class)
-    fun `Javalin#servlet() throws if used after Javalin#configure()`() = TestUtil.test { app, http ->
-        app.configure { }
-    }
-
     @Test
     fun `test all config options`() {
-        val app = Javalin.create()
-        app.configure {
+        val app = Javalin.create {
             it.addSinglePageRoot("/", "/public/html.html")
             it.addSinglePageRoot("/", "src/test/resources/public/html.html", Location.EXTERNAL)
             it.addStaticFiles("/public")
diff --git a/src/test/java/io/javalin/TestContextExtensions.java b/src/test/java/io/javalin/TestContextExtensions.java
index 707781d6..39801cfd 100644
--- a/src/test/java/io/javalin/TestContextExtensions.java
+++ b/src/test/java/io/javalin/TestContextExtensions.java
@@ -9,7 +9,6 @@ package io.javalin;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import io.javalin.misc.SerializeableObject;
-import io.javalin.util.TestUtil;
 import org.junit.Test;
 import static org.assertj.core.api.Assertions.assertThat;
 
diff --git a/src/test/java/io/javalin/TestContextPath.kt b/src/test/java/io/javalin/TestContextPath.kt
index 3907df4c..babb7f11 100644
--- a/src/test/java/io/javalin/TestContextPath.kt
+++ b/src/test/java/io/javalin/TestContextPath.kt
@@ -7,7 +7,6 @@
 package io.javalin
 
 import io.javalin.core.util.Util
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.util.function.BiFunction
@@ -40,7 +39,7 @@ class TestContextPath {
 
     @Test
     fun `router works with context-path`() {
-        val javalin = Javalin.create().configure { it.contextPath = "/context-path" }
+        val javalin = Javalin.create { it.contextPath = "/context-path" }
         TestUtil.test(javalin) { app, http ->
             app.get("/hello") { ctx -> ctx.result("Hello World") }
             assertThat(http.getBody("/hello")).isEqualTo("Not found. Request is below context-path")
@@ -50,7 +49,7 @@ class TestContextPath {
 
     @Test
     fun `router works with multi-level context-path`() {
-        val javalin = Javalin.create().configure { it.contextPath = "/context-path/path-context" }
+        val javalin = Javalin.create { it.contextPath = "/context-path/path-context" }
         TestUtil.test(javalin) { app, http ->
             app.get("/hello") { ctx -> ctx.result("Hello World") }
             assertThat(http.get("/context-path/").status).isEqualTo(404)
@@ -60,7 +59,7 @@ class TestContextPath {
 
     @Test
     fun `static-files work with context-path`() {
-        val javalin = Javalin.create().configure { servlet ->
+        val javalin = Javalin.create { servlet ->
             servlet.addStaticFiles("/public")
             servlet.contextPath = "/context-path"
         }
@@ -72,7 +71,7 @@ class TestContextPath {
 
     @Test
     fun `welcome-files work with context-path`() {
-        val javalin = Javalin.create().configure { servlet ->
+        val javalin = Javalin.create { servlet ->
             servlet.addStaticFiles("/public")
             servlet.contextPath = "/context-path"
         }
diff --git a/src/test/java/io/javalin/TestCookieStore.kt b/src/test/java/io/javalin/TestCookieStore.kt
index 3b1577e6..67c27373 100644
--- a/src/test/java/io/javalin/TestCookieStore.kt
+++ b/src/test/java/io/javalin/TestCookieStore.kt
@@ -7,7 +7,6 @@
 package io.javalin
 
 import io.javalin.rendering.template.TemplateUtil
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.util.*
diff --git a/src/test/java/io/javalin/TestCors.kt b/src/test/java/io/javalin/TestCors.kt
index 31a979cc..5baa4beb 100644
--- a/src/test/java/io/javalin/TestCors.kt
+++ b/src/test/java/io/javalin/TestCors.kt
@@ -15,7 +15,6 @@ import io.javalin.core.util.Header.ACCESS_CONTROL_REQUEST_HEADERS
 import io.javalin.core.util.Header.ACCESS_CONTROL_REQUEST_METHOD
 import io.javalin.core.util.Header.ORIGIN
 import io.javalin.core.util.Header.REFERER
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
@@ -23,12 +22,12 @@ class TestCors {
 
     @Test(expected = IllegalArgumentException::class)
     fun `enableCorsForOrigin() throws for empty varargs`() {
-        Javalin.create().configure { it.enableCorsForOrigin() }
+        Javalin.create { it.enableCorsForOrigin() }
     }
 
     @Test
     fun `enableCorsForOrigin() enables cors for specific origins`() {
-        val javalin = Javalin.create().configure { it.enableCorsForOrigin("origin-1", "referer-1") }
+        val javalin = Javalin.create { it.enableCorsForOrigin("origin-1", "referer-1") }
         TestUtil.test(javalin) { app, http ->
             app.get("/") { ctx -> ctx.result("Hello") }
             assertThat(Unirest.get(http.origin).asString().headers[ACCESS_CONTROL_ALLOW_ORIGIN]).isNull()
@@ -39,7 +38,7 @@ class TestCors {
 
     @Test
     fun `enableCorsForAllOrigins() enables cors for all origins`() {
-        val javalin = Javalin.create().configure { it.enableCorsForAllOrigins() }
+        val javalin = Javalin.create { it.enableCorsForAllOrigins() }
         TestUtil.test(javalin) { app, http ->
             app.get("/") { ctx -> ctx.result("Hello") }
             assertThat(Unirest.get(http.origin).header("Origin", "some-origin").asString().headers[ACCESS_CONTROL_ALLOW_ORIGIN]!![0]).isEqualTo("some-origin")
@@ -49,7 +48,7 @@ class TestCors {
         }
     }
 
-    private val accessManagedCorsApp = Javalin.create().configure {
+    private val accessManagedCorsApp = Javalin.create {
         it.enableCorsForAllOrigins()
         it.accessManager { _, ctx, _ ->
             ctx.status(401).result("Unauthorized")
diff --git a/src/test/java/io/javalin/TestCustomJetty.kt b/src/test/java/io/javalin/TestCustomJetty.kt
index 13bfad72..0774d47e 100644
--- a/src/test/java/io/javalin/TestCustomJetty.kt
+++ b/src/test/java/io/javalin/TestCustomJetty.kt
@@ -9,7 +9,6 @@ package io.javalin
 
 import com.mashape.unirest.http.Unirest
 import io.javalin.util.TestServlet
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.server.Handler
 import org.eclipse.jetty.server.RequestLog
@@ -37,7 +36,7 @@ class TestCustomJetty {
     fun `embedded server can have custom jetty Handler`() {
         val statisticsHandler = StatisticsHandler()
         val newServer = Server().apply { handler = statisticsHandler }
-        val javalin = Javalin.create().configure { it.server { newServer } }
+        val javalin = Javalin.create { it.server { newServer } }
         TestUtil.test(javalin) { app, http ->
             app.get("/") { ctx -> ctx.result("Hello World") }
             val requests = 5
@@ -57,7 +56,7 @@ class TestCustomJetty {
         val requestLogHandler = RequestLogHandler().apply { requestLog = RequestLog { _, _ -> logCount.incrementAndGet() } }
         val handlerChain = StatisticsHandler().apply { handler = requestLogHandler }
         val newServer = Server().apply { handler = handlerChain }
-        val javalin = Javalin.create().configure { it.server { newServer } }
+        val javalin = Javalin.create { it.server { newServer } }
         TestUtil.test(javalin) { app, http ->
             app.get("/") { ctx -> ctx.result("Hello World") }
             val requests = 10
@@ -77,7 +76,7 @@ class TestCustomJetty {
         val handlerCollection = HandlerCollection()
         val handlerChain = StatisticsHandler().apply { handler = handlerCollection }
         val newServer = Server().apply { handler = handlerChain }
-        val javalin = Javalin.create().configure { it.server { newServer } }
+        val javalin = Javalin.create { it.server { newServer } }
         TestUtil.test(javalin) { app, http ->
             app.get("/") { ctx -> ctx.result("Hello World") }
             val requests = 10
@@ -103,8 +102,7 @@ class TestCustomJetty {
                 }
             }
         }
-        val javalin = Javalin.create()
-                .configure {
+        val javalin = Javalin.create {
                     it.sessionHandler { fileSessionHandler }
                     it.server { newServer }
                 }.start(0)
@@ -118,7 +116,7 @@ class TestCustomJetty {
 
     @Test
     fun `default SessionHandler works`() {
-        Javalin.create().configure { it.sessionHandler { SessionHandler() } }
+        Javalin.create { it.sessionHandler { SessionHandler() } }
     }
 
     @Test
@@ -132,7 +130,7 @@ class TestCustomJetty {
                 }.getSessionDataStore(sessionHandler)
             }
         }
-        Javalin.create().configure {
+        Javalin.create {
             it.sessionHandler { sqlSessionHandler(driver = "null", url = "null") }
         }
     }
@@ -152,7 +150,7 @@ class TestCustomJetty {
         }
         newServer.handler = handler
 
-        val javalin = Javalin.create().configure { it.server { newServer } }
+        val javalin = Javalin.create { it.server { newServer } }
         TestUtil.test(javalin) { app, http ->
             app.get("/bar") { ctx -> ctx.result("Hello") }
             assertThat(http.getBody("/foo/foo")).isEqualTo("yo dude")
@@ -172,7 +170,7 @@ class TestCustomJetty {
                 })
             }
         }
-        val javalin = Javalin.create().configure { it.server { newServer } }
+        val javalin = Javalin.create { it.server { newServer } }
         TestUtil.test(javalin) { app, http ->
             app.get("/") { ctx -> ctx.result("Hello Javalin World!") }
             assertThat(http.getBody("/")).contains("Hello Javalin World!")
diff --git a/src/test/java/io/javalin/TestEncoding.kt b/src/test/java/io/javalin/TestEncoding.kt
index b14cb555..0a54822c 100644
--- a/src/test/java/io/javalin/TestEncoding.kt
+++ b/src/test/java/io/javalin/TestEncoding.kt
@@ -9,7 +9,6 @@ package io.javalin
 
 import com.mashape.unirest.http.Unirest
 import io.javalin.core.util.Header
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.net.URLEncoder
@@ -64,13 +63,13 @@ class TestEncoding {
     }
 
     @Test
-    fun `setting a default content-type works`() = TestUtil.test(Javalin.create().configure { it.defaultContentType = "application/json" }) { app, http ->
+    fun `setting a default content-type works`() = TestUtil.test(Javalin.create { it.defaultContentType = "application/json" }) { app, http ->
         app.get("/default") { ctx -> ctx.result("not json") }
         assertThat(http.get("/default").headers.getFirst(Header.CONTENT_TYPE)).contains("application/json")
     }
 
     @Test
-    fun `content-type can be overridden in handler`() = TestUtil.test(Javalin.create().configure { it.defaultContentType = "application/json" }) { app, http ->
+    fun `content-type can be overridden in handler`() = TestUtil.test(Javalin.create { it.defaultContentType = "application/json" }) { app, http ->
         app.get("/override") { ctx ->
             ctx.res.characterEncoding = "utf-8"
             ctx.res.contentType = "text/html"
diff --git a/src/test/java/io/javalin/TestErrorMapper.kt b/src/test/java/io/javalin/TestErrorMapper.kt
index 238448e1..dc0b6be3 100644
--- a/src/test/java/io/javalin/TestErrorMapper.kt
+++ b/src/test/java/io/javalin/TestErrorMapper.kt
@@ -7,7 +7,6 @@
 
 package io.javalin
 
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestEtags.kt b/src/test/java/io/javalin/TestEtags.kt
index 12951f4a..5d2065b0 100644
--- a/src/test/java/io/javalin/TestEtags.kt
+++ b/src/test/java/io/javalin/TestEtags.kt
@@ -8,13 +8,12 @@ package io.javalin
 
 import com.mashape.unirest.http.Unirest
 import io.javalin.core.util.Header
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
 class TestEtags {
 
-    val etagsEnabledApp = Javalin.create().configure { it.autogenerateEtags = true }
+    val etagsEnabledApp = Javalin.create { it.autogenerateEtags = true }
 
     @Test
     fun `default app does not set etags for GET`() = TestUtil.test { app, http ->
diff --git a/src/test/java/io/javalin/TestExceptionMapper.kt b/src/test/java/io/javalin/TestExceptionMapper.kt
index f088cf5a..2649381c 100644
--- a/src/test/java/io/javalin/TestExceptionMapper.kt
+++ b/src/test/java/io/javalin/TestExceptionMapper.kt
@@ -8,7 +8,6 @@
 package io.javalin
 
 import io.javalin.misc.TypedException
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestExtension.java b/src/test/java/io/javalin/TestExtension.java
index 5e0e56b1..ea4015e5 100644
--- a/src/test/java/io/javalin/TestExtension.java
+++ b/src/test/java/io/javalin/TestExtension.java
@@ -5,7 +5,6 @@
  */
 package io.javalin;
 
-import io.javalin.util.TestUtil;
 import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
diff --git a/src/test/java/io/javalin/TestFilters.kt b/src/test/java/io/javalin/TestFilters.kt
index 5ae95763..80d8fb29 100644
--- a/src/test/java/io/javalin/TestFilters.kt
+++ b/src/test/java/io/javalin/TestFilters.kt
@@ -8,7 +8,6 @@
 package io.javalin
 
 import com.mashape.unirest.http.HttpMethod
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestFuture.kt b/src/test/java/io/javalin/TestFuture.kt
index f20dae86..8651f1be 100644
--- a/src/test/java/io/javalin/TestFuture.kt
+++ b/src/test/java/io/javalin/TestFuture.kt
@@ -1,6 +1,5 @@
 package io.javalin
 
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.util.concurrent.CancellationException
diff --git a/src/test/java/io/javalin/TestGracefulShutdown.java b/src/test/java/io/javalin/TestGracefulShutdown.java
index f3807223..2e981afd 100644
--- a/src/test/java/io/javalin/TestGracefulShutdown.java
+++ b/src/test/java/io/javalin/TestGracefulShutdown.java
@@ -30,23 +30,26 @@ public class TestGracefulShutdown {
 
     @Test
     public void t1_shutdown_is_graceful_with_default_config() throws Exception {
-        Javalin app = sharedJavalinConfiguration().start(0);
+        Javalin app = Javalin.create().start(0);
+        addEndpoints(app);
         testIfShutdownIsGraceful(app);
     }
 
     @Test
     public void t2_shutdown_is_graceful_when_custom_server_has_statisticshandler() throws Exception {
-        Javalin app = sharedJavalinConfiguration().configure(c -> c.server(() -> {
+        Javalin app = Javalin.create(c -> c.server(() -> {
             Server server = new Server();
             server.insertHandler(new StatisticsHandler());
             return server;
         })).start(0);
+        addEndpoints(app);
         testIfShutdownIsGraceful(app);
     }
 
     @Test(expected = ExecutionException.class)
     public void t3_shutdown_is_not_graceful_when_custom_server_has_no_statisticshandler() throws Exception {
-        Javalin app = sharedJavalinConfiguration().configure(c -> c.server(Server::new)).start(0);
+        Javalin app = Javalin.create(c -> c.server(Server::new)).start(0);
+        addEndpoints(app);
         testIfShutdownIsGraceful(app); // should throw exception
     }
 
@@ -57,10 +60,9 @@ public class TestGracefulShutdown {
         assertEquals(asyncResponse.get().getStatus(), 200);
     }
 
-    private Javalin sharedJavalinConfiguration() {
-        return Javalin.create().disableStartupBanner()
-            .get("/immediate-response", context -> context.status(200))
-            .get("/delayed-response", context -> Thread.sleep(LONG_WAIT_TIME_IN_MSECS));
+    private void addEndpoints(Javalin app) {
+        app.get("/immediate-response", context -> context.status(200));
+        app.get("/delayed-response", context -> Thread.sleep(LONG_WAIT_TIME_IN_MSECS));
     }
 
     private void performBlockingRequest(Javalin app) throws Exception {
diff --git a/src/test/java/io/javalin/TestGzip.kt b/src/test/java/io/javalin/TestGzip.kt
index 8fcf4026..6a2740c4 100644
--- a/src/test/java/io/javalin/TestGzip.kt
+++ b/src/test/java/io/javalin/TestGzip.kt
@@ -8,7 +8,6 @@ package io.javalin
 
 import com.mashape.unirest.http.Unirest
 import io.javalin.core.util.Header
-import io.javalin.util.TestUtil
 import okhttp3.OkHttpClient
 import okhttp3.Request
 import org.assertj.core.api.Assertions.assertThat
@@ -25,7 +24,7 @@ class TestGzip {
             .get("/huge") { ctx -> ctx.result(getSomeObjects(1000).toString()) }
             .get("/tiny") { ctx -> ctx.result(getSomeObjects(10).toString()) }
 
-    val gzipDisabledApp = Javalin.create().configure { it.dynamicGzip = false }
+    val gzipDisabledApp = Javalin.create { it.dynamicGzip = false }
             .get("/huge") { ctx -> ctx.result(getSomeObjects(1000).toString()) }
             .get("/tiny") { ctx -> ctx.result(getSomeObjects(10).toString()) }
 
diff --git a/src/test/java/io/javalin/TestHttpResponseExceptions.kt b/src/test/java/io/javalin/TestHttpResponseExceptions.kt
index 7d768b36..18a51b2f 100644
--- a/src/test/java/io/javalin/TestHttpResponseExceptions.kt
+++ b/src/test/java/io/javalin/TestHttpResponseExceptions.kt
@@ -7,7 +7,6 @@
 package io.javalin
 
 import io.javalin.core.util.Header
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.http.HttpStatus
 import org.junit.Test
@@ -154,7 +153,7 @@ class TestHttpResponseExceptions {
     }
 
     @Test
-    fun `default content type affects http response errors`() = TestUtil.test(Javalin.create().configure { it.defaultContentType = "application/json" }) { app, http ->
+    fun `default content type affects http response errors`() = TestUtil.test(Javalin.create { it.defaultContentType = "application/json" }) { app, http ->
         app.get("/content-type") { throw ForbiddenResponse() }
         val response = http.get("/content-type")
         assertThat(response.status).isEqualTo(HttpStatus.FORBIDDEN_403)
diff --git a/src/test/java/io/javalin/TestHttpVerbs.kt b/src/test/java/io/javalin/TestHttpVerbs.kt
index 93241d79..8c80bd00 100644
--- a/src/test/java/io/javalin/TestHttpVerbs.kt
+++ b/src/test/java/io/javalin/TestHttpVerbs.kt
@@ -8,8 +8,7 @@
 package io.javalin
 
 import com.mashape.unirest.http.HttpMethod
-import io.javalin.util.TestUtil
-import io.javalin.util.TestUtil.okHandler
+import io.javalin.TestUtil.okHandler
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestJson.kt b/src/test/java/io/javalin/TestJson.kt
index c1f37150..e844b19a 100644
--- a/src/test/java/io/javalin/TestJson.kt
+++ b/src/test/java/io/javalin/TestJson.kt
@@ -15,7 +15,6 @@ import io.javalin.json.ToJsonMapper
 import io.javalin.misc.CustomMapper
 import io.javalin.misc.NonSerializableObject
 import io.javalin.misc.SerializeableObject
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Before
 import org.junit.Test
diff --git a/src/test/java/io/javalin/TestLifecycleEvents.kt b/src/test/java/io/javalin/TestLifecycleEvents.kt
index bbe64da7..eec1645c 100644
--- a/src/test/java/io/javalin/TestLifecycleEvents.kt
+++ b/src/test/java/io/javalin/TestLifecycleEvents.kt
@@ -7,7 +7,6 @@
 
 package io.javalin
 
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestLogging.kt b/src/test/java/io/javalin/TestLogging.kt
index e709e449..4f02181f 100644
--- a/src/test/java/io/javalin/TestLogging.kt
+++ b/src/test/java/io/javalin/TestLogging.kt
@@ -7,7 +7,6 @@
 package io.javalin
 
 import io.javalin.util.HttpUtil
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.util.concurrent.CompletableFuture
@@ -20,10 +19,10 @@ class TestLogging {
     fun `default logging (no logging) works`() = runTest(Javalin.create())
 
     @Test
-    fun `debug logging works`() = runTest(Javalin.create().enableDevLogging())
+    fun `debug logging works`() = runTest(Javalin.create { it.enableDevLogging() })
 
     @Test
-    fun `custom logging works`() = runTest(Javalin.create().configure {
+    fun `custom logging works`() = runTest(Javalin.create {
         it.requestLogger { _, executionTimeMs ->
             println("That took $executionTimeMs milliseconds")
         }
@@ -44,7 +43,7 @@ class TestLogging {
     }
 
     private val loggerLog = mutableListOf<String?>()
-    private val bodyLoggingJavalin = Javalin.create().configure {
+    private val bodyLoggingJavalin = Javalin.create {
         it.requestLogger { ctx, ms ->
             loggerLog.add(ctx.resultString())
             loggerLog.add(ctx.resultString())
diff --git a/src/test/java/io/javalin/TestMethodNotAllowed.kt b/src/test/java/io/javalin/TestMethodNotAllowed.kt
index 2d01a75a..0bde0f90 100644
--- a/src/test/java/io/javalin/TestMethodNotAllowed.kt
+++ b/src/test/java/io/javalin/TestMethodNotAllowed.kt
@@ -1,13 +1,12 @@
 package io.javalin
 
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import javax.servlet.http.HttpServletResponse
 
 class TestMethodNotAllowed {
 
-    private val preferring405Javalin = Javalin.create().configure { it.prefer405over404 = true }.apply {
+    private val preferring405Javalin = Javalin.create { it.prefer405over404 = true }.apply {
         post("/test") { ctx -> ctx.result("Hello world") }
         put("/test") { ctx -> ctx.result("Hello world") }
         delete("/test") { ctx -> ctx.result("Hello world") }
diff --git a/src/test/java/io/javalin/TestMultipartForms.kt b/src/test/java/io/javalin/TestMultipartForms.kt
index ac6e06b5..46e1802c 100644
--- a/src/test/java/io/javalin/TestMultipartForms.kt
+++ b/src/test/java/io/javalin/TestMultipartForms.kt
@@ -8,7 +8,6 @@ package io.javalin
 
 import io.javalin.json.JavalinJackson
 import io.javalin.misc.UploadInfo
-import io.javalin.util.TestUtil
 import okhttp3.*
 import org.apache.commons.io.IOUtils
 import org.assertj.core.api.Assertions.assertThat
diff --git a/src/test/java/io/javalin/TestRequest.kt b/src/test/java/io/javalin/TestRequest.kt
index 5a4f3624..5f5779c9 100644
--- a/src/test/java/io/javalin/TestRequest.kt
+++ b/src/test/java/io/javalin/TestRequest.kt
@@ -8,7 +8,6 @@ package io.javalin
 
 import com.mashape.unirest.http.Unirest
 import io.javalin.core.util.Header
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
@@ -265,7 +264,7 @@ class TestRequest {
     }
 
     @Test
-    fun `contextPath() with value works`() = TestUtil.test(Javalin.create().configure { it.contextPath = "/ctx" }) { app, http ->
+    fun `contextPath() with value works`() = TestUtil.test(Javalin.create { it.contextPath = "/ctx" }) { app, http ->
         app.get("/") { ctx -> ctx.result(ctx.contextPath()) }
         assertThat(http.getBody("/ctx/")).isEqualTo("/ctx")
     }
diff --git a/src/test/java/io/javalin/TestRequestCache.kt b/src/test/java/io/javalin/TestRequestCache.kt
index 7568f89b..34bfff57 100644
--- a/src/test/java/io/javalin/TestRequestCache.kt
+++ b/src/test/java/io/javalin/TestRequestCache.kt
@@ -1,6 +1,5 @@
 package io.javalin
 
-import io.javalin.util.TestUtil
 import org.apache.http.client.methods.HttpPost
 import org.apache.http.entity.ByteArrayEntity
 import org.apache.http.impl.client.HttpClients
@@ -24,7 +23,7 @@ class TestRequestCache {
     }
 
     @Test
-    fun `disabling request-caching works`() = TestUtil.test(Javalin.create().configure { it.requestCacheSize = 0 }) { app, http ->
+    fun `disabling request-caching works`() = TestUtil.test(Javalin.create { it.requestCacheSize = 0 }) { app, http ->
         app.post("/disabled-cache") { ctx ->
             if (ctx.req.inputStream.javaClass.simpleName == "CachedServletInputStream") {
                 throw IllegalStateException("Cache should be disabled.")
diff --git a/src/test/java/io/javalin/TestResponse.kt b/src/test/java/io/javalin/TestResponse.kt
index 338e95b7..713bbe40 100644
--- a/src/test/java/io/javalin/TestResponse.kt
+++ b/src/test/java/io/javalin/TestResponse.kt
@@ -9,7 +9,6 @@ package io.javalin
 
 import com.mashape.unirest.http.HttpMethod
 import io.javalin.core.util.Header
-import io.javalin.util.TestUtil
 import org.apache.commons.io.FileUtils
 import org.apache.commons.io.IOUtils
 import org.assertj.core.api.Assertions.assertThat
diff --git a/src/test/java/io/javalin/TestRouting.kt b/src/test/java/io/javalin/TestRouting.kt
index efffc268..2ea7b22f 100644
--- a/src/test/java/io/javalin/TestRouting.kt
+++ b/src/test/java/io/javalin/TestRouting.kt
@@ -9,7 +9,6 @@ package io.javalin
 
 import io.javalin.apibuilder.ApiBuilder.get
 import io.javalin.apibuilder.ApiBuilder.path
-import io.javalin.util.TestUtil
 import okhttp3.OkHttpClient
 import okhttp3.Request
 import org.assertj.core.api.Assertions.assertThat
@@ -86,7 +85,7 @@ class TestRouting {
     }
 
     @Test
-    fun `enforceSsl works`() = TestUtil.test(Javalin.create().configure { it.enforceSsl = true }) { app, http ->
+    fun `enforceSsl works`() = TestUtil.test(Javalin.create { it.enforceSsl = true }) { app, http ->
         app.get("/ssl") {}
         http.disableUnirestRedirects()
         assertThat(http.get("/ssl").status).isEqualTo(301)
diff --git a/src/test/java/io/javalin/TestSinglePageMode.kt b/src/test/java/io/javalin/TestSinglePageMode.kt
index 8410cd53..3a404fdf 100644
--- a/src/test/java/io/javalin/TestSinglePageMode.kt
+++ b/src/test/java/io/javalin/TestSinglePageMode.kt
@@ -10,7 +10,6 @@ import com.mashape.unirest.http.Unirest
 import io.javalin.core.util.Header
 import io.javalin.core.util.OptionalDependency
 import io.javalin.staticfiles.Location
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.io.File
@@ -18,7 +17,7 @@ import java.io.File
 class TestSinglePageMode {
 
     private val rootSinglePageApp_classPath: Javalin by lazy {
-        Javalin.create().configure {
+        Javalin.create {
             it.addStaticFiles("/public")
             it.addSinglePageRoot("/", "/public/html.html")
             it.enableWebjars()
@@ -26,7 +25,7 @@ class TestSinglePageMode {
     }
 
     private val dualSinglePageApp_classPath: Javalin by lazy {
-        Javalin.create().configure {
+        Javalin.create {
             it.addStaticFiles("/public")
             it.addSinglePageRoot("/admin", "/public/protected/secret.html")
             it.addSinglePageRoot("/public", "/public/html.html")
@@ -34,7 +33,7 @@ class TestSinglePageMode {
 
     }
     private val rootSinglePageApp_external: Javalin by lazy {
-        Javalin.create().configure {
+        Javalin.create {
             it.addSinglePageRoot("/", "src/test/external/html.html", Location.EXTERNAL)
         }
     }
@@ -81,12 +80,12 @@ class TestSinglePageMode {
 
     @Test(expected = IllegalArgumentException::class)
     fun `SinglePageHandler throws for non-existent file (classpath)`() {
-        Javalin.create().configure { it.addSinglePageRoot("/", "/not-a-file.html") }.start().stop()
+        Javalin.create { it.addSinglePageRoot("/", "/not-a-file.html") }.start().stop()
     }
 
     @Test(expected = IllegalArgumentException::class)
     fun `SinglePageHandler throws for non-existent file (external)`() {
-        Javalin.create().configure { it.addSinglePageRoot("/", "/not-a-file.html", Location.EXTERNAL) }.start().stop()
+        Javalin.create { it.addSinglePageRoot("/", "/not-a-file.html", Location.EXTERNAL) }.start().stop()
     }
 
     @Test
@@ -100,7 +99,7 @@ class TestSinglePageMode {
     fun `SinglePageHandler doesn't cache on localhost`() {
         val filePath = "src/test/external/my-special-file.html"
         val file = File(filePath).apply { createNewFile() }.apply { writeText("OLD FILE") }
-        val app = Javalin.create().configure { it.addSinglePageRoot("/", filePath, Location.EXTERNAL) }.start(0)
+        val app = Javalin.create { it.addSinglePageRoot("/", filePath, Location.EXTERNAL) }.start(0)
         fun getSpaPage() = Unirest.get("http://localhost:${app.port()}/").header(Header.ACCEPT, "text/html").asString().body
         assertThat(getSpaPage()).contains("OLD FILE")
         file.writeText("NEW FILE")
diff --git a/src/test/java/io/javalin/TestSse.java b/src/test/java/io/javalin/TestSse.java
index 01551ee0..00a8de29 100644
--- a/src/test/java/io/javalin/TestSse.java
+++ b/src/test/java/io/javalin/TestSse.java
@@ -2,7 +2,6 @@ package io.javalin;
 
 import com.mashape.unirest.http.Headers;
 import io.javalin.serversentevent.SseClient;
-import io.javalin.util.TestUtil;
 import java.util.ArrayList;
 import java.util.List;
 import org.junit.Test;
diff --git a/src/test/java/io/javalin/TestStaticDirectorySlash.kt b/src/test/java/io/javalin/TestStaticDirectorySlash.kt
index 800bd736..b8e4febb 100644
--- a/src/test/java/io/javalin/TestStaticDirectorySlash.kt
+++ b/src/test/java/io/javalin/TestStaticDirectorySlash.kt
@@ -8,13 +8,12 @@
 
 package io.javalin
 
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
 class TestStaticDirectorySlash {
 
-    private val normalJavalin = Javalin.create().configure { it.addStaticFiles("public") }
+    private val normalJavalin = Javalin.create { it.addStaticFiles("public") }
 
     @Test
     fun `normal javalin ignores static directory slashes`() = TestUtil.test(normalJavalin) { _, http ->
diff --git a/src/test/java/io/javalin/TestStaticFiles.kt b/src/test/java/io/javalin/TestStaticFiles.kt
index 28a3888e..6ddd497e 100644
--- a/src/test/java/io/javalin/TestStaticFiles.kt
+++ b/src/test/java/io/javalin/TestStaticFiles.kt
@@ -10,21 +10,23 @@ package io.javalin
 import io.javalin.core.util.Header
 import io.javalin.core.util.OptionalDependency
 import io.javalin.staticfiles.Location
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
 class TestStaticFiles {
 
-    private val defaultStaticResourceApp = Javalin.create().configure { it.addStaticFiles("/public") } // classpath
-    private val externalStaticResourceApp = Javalin.create().configure { it.addStaticFiles("src/test/external/", Location.EXTERNAL) }
-    private val multiLocationStaticResourceApp = Javalin.create().configure { servlet ->
+    private val defaultStaticResourceApp = Javalin.create { it.addStaticFiles("/public") } // classpath
+    private val externalStaticResourceApp = Javalin.create { it.addStaticFiles("src/test/external/", Location.EXTERNAL) }
+    private val multiLocationStaticResourceApp = Javalin.create { servlet ->
         servlet.addStaticFiles("src/test/external/", Location.EXTERNAL)
         servlet.addStaticFiles("/public/immutable")
         servlet.addStaticFiles("/public/protected")
         servlet.addStaticFiles("/public/subdir")
     }
-    private val devLoggingApp = Javalin.create().configure { it.addStaticFiles("/public") }.enableDevLogging()
+    private val devLoggingApp = Javalin.create {
+        it.addStaticFiles("/public")
+        it.enableDevLogging()
+    }
 
     @Test
     fun `serving HTML from classpath works`() = TestUtil.test(defaultStaticResourceApp) { _, http ->
@@ -103,7 +105,7 @@ class TestStaticFiles {
     }
 
     @Test
-    fun `WebJars available if enabled`() = TestUtil.test(Javalin.create().configure { it.enableWebjars() }) { _, http ->
+    fun `WebJars available if enabled`() = TestUtil.test(Javalin.create { it.enableWebjars() }) { _, http ->
         assertThat(http.get("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").status).isEqualTo(200)
         assertThat(http.get("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").headers.getFirst(Header.CONTENT_TYPE)).contains("text/css")
         assertThat(http.get("/webjars/swagger-ui/${OptionalDependency.SWAGGERUI.version}/swagger-ui.css").headers.getFirst(Header.CACHE_CONTROL)).isEqualTo("max-age=31622400")
diff --git a/src/test/java/io/javalin/TestStaticFilesEdgeCases.kt b/src/test/java/io/javalin/TestStaticFilesEdgeCases.kt
index a20f59ce..bb4e7e18 100644
--- a/src/test/java/io/javalin/TestStaticFilesEdgeCases.kt
+++ b/src/test/java/io/javalin/TestStaticFilesEdgeCases.kt
@@ -16,24 +16,24 @@ class TestStaticFilesEdgeCases {
 
     @Test(expected = RuntimeException::class)
     fun `server doesn't start for non-existent classpath folder`() {
-        Javalin.create().configure { it.addStaticFiles("some-fake-folder") }.start()
+        Javalin.create { it.addStaticFiles("some-fake-folder") }.start()
     }
 
     @Test(expected = RuntimeException::class)
     fun `server doesn't start for non-existent external folder`() {
-        Javalin.create().configure { it.addStaticFiles("some-fake-folder", Location.EXTERNAL) }.start()
+        Javalin.create { it.addStaticFiles("some-fake-folder", Location.EXTERNAL) }.start()
     }
 
     @Test(expected = RuntimeException::class)
     fun `server doesn't start for empty classpath folder`() {
         File("src/test/external/empty").mkdir()
-        Javalin.create().configure { it.addStaticFiles("src/test/external/empty", Location.CLASSPATH) }.start()
+        Javalin.create { it.addStaticFiles("src/test/external/empty", Location.CLASSPATH) }.start()
     }
 
     @Test
     fun `server starts for empty external folder`() {
         File("src/test/external/empty").mkdir()
-        Javalin.create().configure { it.addStaticFiles("src/test/external/empty", Location.EXTERNAL) }.start(0).stop()
+        Javalin.create { it.addStaticFiles("src/test/external/empty", Location.EXTERNAL) }.start(0).stop()
     }
 
     @Test
diff --git a/src/test/java/io/javalin/TestTemplates.kt b/src/test/java/io/javalin/TestTemplates.kt
index bf8a6c0b..056d85dc 100644
--- a/src/test/java/io/javalin/TestTemplates.kt
+++ b/src/test/java/io/javalin/TestTemplates.kt
@@ -15,7 +15,6 @@ import io.javalin.rendering.template.JavalinJtwig
 import io.javalin.rendering.template.JavalinPebble
 import io.javalin.rendering.template.JavalinVelocity
 import io.javalin.rendering.template.TemplateUtil.model
-import io.javalin.util.TestUtil
 import org.apache.velocity.app.VelocityEngine
 import org.assertj.core.api.Assertions.assertThat
 import org.jtwig.environment.EnvironmentConfigurationBuilder
diff --git a/src/test/java/io/javalin/TestTrailingSlashes.kt b/src/test/java/io/javalin/TestTrailingSlashes.kt
index 838bc0d4..0bc9cc51 100644
--- a/src/test/java/io/javalin/TestTrailingSlashes.kt
+++ b/src/test/java/io/javalin/TestTrailingSlashes.kt
@@ -9,7 +9,6 @@ package io.javalin
 
 import io.javalin.apibuilder.ApiBuilder.get
 import io.javalin.apibuilder.ApiBuilder.path
-import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
diff --git a/src/test/java/io/javalin/TestUnixSocketConnector.kt b/src/test/java/io/javalin/TestUnixSocketConnector.kt
index 5e8d04f3..62c55af0 100644
--- a/src/test/java/io/javalin/TestUnixSocketConnector.kt
+++ b/src/test/java/io/javalin/TestUnixSocketConnector.kt
@@ -1,6 +1,5 @@
 package io.javalin
 
-import io.javalin.util.TestUtil
 import jnr.unixsocket.UnixSocketAddress
 import jnr.unixsocket.UnixSocketChannel
 import org.assertj.core.api.Assertions.assertThat
@@ -23,7 +22,7 @@ class TestUnixSocketConnector {
         val testPath = "/unixsocket"
         val expectedResultString = "hello unixsocket"
 
-        val unixSocketJavalin = Javalin.create().configure {
+        val unixSocketJavalin = Javalin.create {
             it.server {
                 val server = Server()
                 val serverConnector = ServerConnector(server)
diff --git a/src/test/java/io/javalin/util/TestUtil.java b/src/test/java/io/javalin/TestUtil.java
similarity index 86%
rename from src/test/java/io/javalin/util/TestUtil.java
rename to src/test/java/io/javalin/TestUtil.java
index e664fefe..17a80085 100644
--- a/src/test/java/io/javalin/util/TestUtil.java
+++ b/src/test/java/io/javalin/TestUtil.java
@@ -4,20 +4,23 @@
  * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
  */
 
-package io.javalin.util;
+package io.javalin;
 
 import com.mashape.unirest.http.HttpMethod;
 import io.javalin.Handler;
 import io.javalin.Javalin;
 import io.javalin.json.JavalinJackson;
 import io.javalin.json.JavalinJson;
+import io.javalin.util.HttpUtil;
+import io.javalin.util.ThrowingBiConsumer;
 
 public class TestUtil {
 
     public static Handler okHandler = ctx -> ctx.result("OK");
 
     public static void test(Javalin javalin, ThrowingBiConsumer<Javalin, HttpUtil> test) {
-        javalin.disableStartupBanner().start(0);
+        javalin.config.showJavalinBanner = false;
+        javalin.start(0);
         HttpUtil http = new HttpUtil(javalin);
         test.accept(javalin, http);
         javalin.delete("/x-test-cookie-cleaner", ctx -> ctx.cookieMap().keySet().forEach(ctx::removeCookie));
diff --git a/src/test/java/io/javalin/TestWebSocket.kt b/src/test/java/io/javalin/TestWebSocket.kt
index bfc281d3..741dee30 100644
--- a/src/test/java/io/javalin/TestWebSocket.kt
+++ b/src/test/java/io/javalin/TestWebSocket.kt
@@ -10,7 +10,6 @@ import com.mashape.unirest.http.Unirest
 import io.javalin.apibuilder.ApiBuilder.ws
 import io.javalin.json.JavalinJson
 import io.javalin.misc.SerializeableObject
-import io.javalin.util.TestUtil
 import io.javalin.websocket.WsContext
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.websocket.api.MessageTooLargeException
@@ -36,31 +35,28 @@ class TestWebSocket {
         return this.attribute(TestLogger::class.java)
     }
 
-    fun contextPathJavalin() = Javalin.create().configure { it.wsContextPath = "/websocket" }
+    fun contextPathJavalin() = Javalin.create { it.wsContextPath = "/websocket" }
 
     fun javalinWithWsLogger() = Javalin.create().apply {
-        this.configure { config ->
-            config.wsLogger { ws ->
-                ws.onConnect { ctx -> this.logger().log.add(ctx.pathParam("param") + " connected") }
-                ws.onClose { ctx -> this.logger().log.add(ctx.pathParam("param") + " disconnected") }
-            }
+        this.config.wsLogger { ws ->
+            ws.onConnect { ctx -> this.logger().log.add(ctx.pathParam("param") + " connected") }
+            ws.onClose { ctx -> this.logger().log.add(ctx.pathParam("param") + " disconnected") }
         }
     }
 
     fun accessManagedJavalin() = Javalin.create().apply {
-        this.configure {
-            it.accessManager { handler, ctx, permittedRoles ->
-                this.logger().log.add("handling upgrade request ...")
-                when {
-                    ctx.queryParam("allowed") == "true" -> {
-                        this.logger().log.add("upgrade request valid!")
-                        handler.handle(ctx)
-                    }
-                    ctx.queryParam("exception") == "true" -> throw UnauthorizedResponse()
-                    else -> this.logger().log.add("upgrade request invalid!")
+        this.config.accessManager { handler, ctx, permittedRoles ->
+            this.logger().log.add("handling upgrade request ...")
+            when {
+                ctx.queryParam("allowed") == "true" -> {
+                    this.logger().log.add("upgrade request valid!")
+                    handler.handle(ctx)
                 }
+                ctx.queryParam("exception") == "true" -> throw UnauthorizedResponse()
+                else -> this.logger().log.add("upgrade request invalid!")
             }
-        }.ws("/*") { ws ->
+        }
+        this.ws("/*") { ws ->
             ws.onConnect { this.logger().log.add("connected with upgrade request") }
         }
     }
@@ -304,7 +300,7 @@ class TestWebSocket {
     }
 
     @Test
-    fun `dev logging works for web sockets`() = TestUtil.test(Javalin.create().enableDevLogging()) { app, _ ->
+    fun `dev logging works for web sockets`() = TestUtil.test(Javalin.create { it.enableDevLogging() }) { app, _ ->
         app.ws("/path/:param") {}
         TestClient(app, "/path/0").connectAndDisconnect()
         TestClient(app, "/path/1?test=banana&hi=1&hi=2").connectAndDisconnect()
@@ -330,7 +326,7 @@ class TestWebSocket {
         val maxTextSize = 1
         val textToSend = "This text is far too long."
         val expectedMessage = "Text message size [${textToSend.length}] exceeds maximum size [$maxTextSize]"
-        val app = Javalin.create().configure {
+        val app = Javalin.create {
             it.wsFactoryConfig { wsFactory ->
                 wsFactory.policy.maxTextMessageSize = maxTextSize
             }
diff --git a/src/test/java/io/javalin/examples/HelloWorldAuth.java b/src/test/java/io/javalin/examples/HelloWorldAuth.java
index e0074f82..070c1b35 100644
--- a/src/test/java/io/javalin/examples/HelloWorldAuth.java
+++ b/src/test/java/io/javalin/examples/HelloWorldAuth.java
@@ -19,7 +19,7 @@ import static io.javalin.security.SecurityUtil.roles;
 public class HelloWorldAuth {
 
     public static void main(String[] args) {
-        Javalin.create().configure(config -> {
+        Javalin.create(config -> {
             config.accessManager((handler, ctx, permittedRoles) -> {
                 String userRole = ctx.queryParam("role");
                 if (userRole != null && permittedRoles.contains(MyRoles.valueOf(userRole))) {
diff --git a/src/test/java/io/javalin/examples/HelloWorldCors.java b/src/test/java/io/javalin/examples/HelloWorldCors.java
index 66a9a549..e4e3cefc 100644
--- a/src/test/java/io/javalin/examples/HelloWorldCors.java
+++ b/src/test/java/io/javalin/examples/HelloWorldCors.java
@@ -15,7 +15,7 @@ public class HelloWorldCors {
 
     public static void main(String[] args) {
 
-        Javalin corsApp = Javalin.create().configure(config -> {
+        Javalin corsApp = Javalin.create(config -> {
             config.enableCorsForOrigin("http://localhost:7001/", "http://localhost:7002");
         }).start(7070);
 
diff --git a/src/test/java/io/javalin/examples/HelloWorldSecure.java b/src/test/java/io/javalin/examples/HelloWorldSecure.java
index 63751d50..dc182f51 100644
--- a/src/test/java/io/javalin/examples/HelloWorldSecure.java
+++ b/src/test/java/io/javalin/examples/HelloWorldSecure.java
@@ -18,7 +18,7 @@ public class HelloWorldSecure {
     // This is a very basic example, a better one can be found at:
     // https://github.com/eclipse/jetty.project/blob/jetty-9.4.x/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java#L139-L163
     public static void main(String[] args) {
-        Javalin.create().configure(config -> {
+        Javalin.create(config -> {
             config.server(() -> {
                 Server server = new Server();
                 ServerConnector sslConnector = new ServerConnector(server, getSslContextFactory());
diff --git a/src/test/java/io/javalin/examples/HelloWorldServlet.java b/src/test/java/io/javalin/examples/HelloWorldServlet.java
index 7a649a57..fb9f337e 100644
--- a/src/test/java/io/javalin/examples/HelloWorldServlet.java
+++ b/src/test/java/io/javalin/examples/HelloWorldServlet.java
@@ -16,8 +16,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
 public class HelloWorldServlet {
 
     public static void main(String[] args) {
-        Javalin app = Javalin.create();
-        app.configure(config -> {
+        Javalin app = Javalin.create(config -> {
             config.server(() -> {
                 Server server = new Server();
                 ServletContextHandler context = new ServletContextHandler();
diff --git a/src/test/java/io/javalin/examples/HelloWorldStaticFiles.java b/src/test/java/io/javalin/examples/HelloWorldStaticFiles.java
index 808ed31a..060b2b3e 100644
--- a/src/test/java/io/javalin/examples/HelloWorldStaticFiles.java
+++ b/src/test/java/io/javalin/examples/HelloWorldStaticFiles.java
@@ -12,7 +12,7 @@ import io.javalin.Javalin;
 public class HelloWorldStaticFiles {
 
     public static void main(String[] args) {
-        Javalin.create().configure(config -> {
+        Javalin.create(config -> {
             config.addStaticFiles("/public");
         }).start(7070);
     }
diff --git a/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java b/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java
index f5f6f2f5..99f924d4 100644
--- a/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java
+++ b/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java
@@ -12,7 +12,7 @@ import io.javalin.staticfiles.Location;
 public class HelloWorldStaticFiles_external {
 
     public static void main(String[] args) {
-        Javalin.create().configure(config -> {
+        Javalin.create(config -> {
             config.addStaticFiles("src/test/external/", Location.EXTERNAL);
         }).start(7070);
     }
diff --git a/src/test/java/io/javalin/examples/HelloWorldWebSockets.java b/src/test/java/io/javalin/examples/HelloWorldWebSockets.java
index 1f333d3d..3891f94a 100644
--- a/src/test/java/io/javalin/examples/HelloWorldWebSockets.java
+++ b/src/test/java/io/javalin/examples/HelloWorldWebSockets.java
@@ -12,7 +12,7 @@ import io.javalin.Javalin;
 // see HelloWorldSecure for how to set that up
 public class HelloWorldWebSockets {
     public static void main(String[] args) {
-        Javalin app = Javalin.create().enableDevLogging();
+        Javalin app = Javalin.create(config -> config.enableDevLogging());
         app.ws("/websocket", ws -> {
             ws.onConnect(ctx -> {
                 System.out.println("Connected");
diff --git a/src/test/java/io/javalin/performance/StupidAsyncTest.java b/src/test/java/io/javalin/performance/StupidAsyncTest.java
index 9987e21e..845ad20a 100644
--- a/src/test/java/io/javalin/performance/StupidAsyncTest.java
+++ b/src/test/java/io/javalin/performance/StupidAsyncTest.java
@@ -32,9 +32,7 @@ public class StupidAsyncTest {
 
         QueuedThreadPool threadPool = new QueuedThreadPool(10, 2, 60_000);
 
-        Javalin app = Javalin.create()
-            .configure(c -> c.server(() -> new Server(threadPool)))
-            .start(0);
+        Javalin app = Javalin.create(c -> c.server(() -> new Server(threadPool))).start(0);
 
         HttpUtil http = new HttpUtil(app);
 
diff --git a/src/test/java/io/javalin/routeoverview/VisualTest.java b/src/test/java/io/javalin/routeoverview/VisualTest.java
index c6791f87..84c36ee7 100644
--- a/src/test/java/io/javalin/routeoverview/VisualTest.java
+++ b/src/test/java/io/javalin/routeoverview/VisualTest.java
@@ -27,12 +27,11 @@ public class VisualTest {
     };
 
     public static void main(String[] args) {
-        Javalin app = Javalin.create()
-            .enableRouteOverview("/route-overview")
-            .configure((config) -> {
-                config.contextPath = "/context-path";
-                config.enableCorsForAllOrigins();
-            }).start();
+        Javalin app = Javalin.create((config) -> {
+            config.contextPath = "/context-path";
+            config.enableCorsForAllOrigins();
+            config.enableRouteOverview("/route-overview");
+        }).start();
 
         app.get("/", ctx -> ctx.redirect("/context-path/route-overview"));
         app.get("/just-some-path", new HandlerImplementation());
diff --git a/src/test/java/io/javalin/validation/TestValidation.kt b/src/test/java/io/javalin/validation/TestValidation.kt
index fdced89d..95ec91c2 100644
--- a/src/test/java/io/javalin/validation/TestValidation.kt
+++ b/src/test/java/io/javalin/validation/TestValidation.kt
@@ -9,7 +9,7 @@ package io.javalin.validation
 import io.javalin.BadRequestResponse
 import io.javalin.json.JavalinJson
 import io.javalin.misc.SerializeableObject
-import io.javalin.util.TestUtil
+import io.javalin.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.eclipse.jetty.http.HttpStatus
 import org.junit.Test
diff --git a/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt b/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt
index 5d5adfc0..fad2ef46 100644
--- a/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt
+++ b/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt
@@ -19,11 +19,10 @@ fun main(args: Array<String>) {
     val counterClients = ConcurrentLinkedQueue<SseClient>()
     val statsClients = ConcurrentLinkedQueue<SseClient>()
 
-    Javalin.create().apply {
-        configure {
-            it.addStaticFiles("/public")
-            it.server { Server(tp) }
-        }
+    Javalin.create {
+        it.addStaticFiles("/public")
+        it.server { Server(tp) }
+    }.apply {
         get("/") { it.redirect("/sse/sse-example.html") }
         sse("/sse-counter") { client ->
             counterClients.add(client)
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldAsync.kt b/src/test/kotlin/io/javalin/examples/HelloWorldAsync.kt
index 19615e60..3d949214 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldAsync.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldAsync.kt
@@ -14,7 +14,7 @@ import java.util.concurrent.Executors
 import java.util.concurrent.TimeUnit
 
 fun main(args: Array<String>) {
-    val app = Javalin.create().enableDevLogging().start(7070)
+    val app = Javalin.create { it.enableDevLogging() }.start(7070)
 
     app.routes {
         get("/result") { ctx ->
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt b/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
index b54a508b..9d9dbd90 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt
@@ -21,9 +21,7 @@ enum class MyRoles : Role {
 
 fun main(args: Array<String>) {
 
-    val app = Javalin.create().apply {
-        configure { it.accessManager(::accessManager) }
-    }.start(7070)
+    val app = Javalin.create { it.accessManager(::accessManager) }.start(7070)
 
     app.routes {
         get("/hello", { ctx -> ctx.result("Hello World 1") }, roles(ROLE_ONE))
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldCors.kt b/src/test/kotlin/io/javalin/examples/HelloWorldCors.kt
index 75bd9655..110bd380 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldCors.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldCors.kt
@@ -11,9 +11,7 @@ import io.javalin.apibuilder.ApiBuilder.*
 
 fun main(args: Array<String>) {
 
-    val corsApp = Javalin.create().apply {
-        configure { it.enableCorsForOrigin("http://localhost:7001/", "http://localhost:7002") }
-    }.start(7070)
+    val corsApp = Javalin.create { it.enableCorsForOrigin("http://localhost:7001/", "http://localhost:7002") }.start(7070)
 
     corsApp.routes {
         get { ctx -> ctx.json("Hello Get") }
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldSecure.kt b/src/test/kotlin/io/javalin/examples/HelloWorldSecure.kt
index 27eac23b..b374bd10 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldSecure.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldSecure.kt
@@ -15,7 +15,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory
 // This is a very basic example, a better one can be found at:
 // https://github.com/eclipse/jetty.project/blob/jetty-9.4.x/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java#L139-L163
 fun main(args: Array<String>) {
-    val app = Javalin.create().configure {
+    val app = Javalin.create {
         it.server {
             val server = Server()
             val sslConnector = ServerConnector(server, sslContextFactory())
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles.kt b/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles.kt
index 7671846f..91a0b1d0 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles.kt
@@ -9,8 +9,6 @@ package io.javalin.examples
 import io.javalin.Javalin
 
 fun main(args: Array<String>) {
-    Javalin.create().apply {
-        configure { it.addStaticFiles("/public")  }
-    }.start(7070)
+    Javalin.create { it.addStaticFiles("/public") }.start(7070)
 }
 
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt b/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt
index 1ad50fc4..b1f35fb7 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt
@@ -10,7 +10,7 @@ import io.javalin.Javalin
 import io.javalin.staticfiles.Location
 
 fun main(args: Array<String>) {
-    Javalin.create().apply {
-        configure { it.addStaticFiles("src/test/external/", Location.EXTERNAL) }
+    Javalin.create {
+        it.addStaticFiles("src/test/external/", Location.EXTERNAL)
     }.start(7070)
 }
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldSwagger.kt b/src/test/kotlin/io/javalin/examples/HelloWorldSwagger.kt
index 1d12fbef..3c1e6bfc 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldSwagger.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldSwagger.kt
@@ -16,8 +16,7 @@ import io.javalin.Javalin
 import io.javalin.core.util.SwaggerRenderer
 
 fun main(args: Array<String>) {
-    Javalin.create().apply {
-        configure { it.enableWebjars() }
+    Javalin.create { it.enableWebjars() }.apply {
         get("/my-docs", SwaggerRenderer("exampleApiSpec.yaml"))
     }.start(0)
 }
diff --git a/src/test/kotlin/io/javalin/examples/HelloWorldWebSockets.kt b/src/test/kotlin/io/javalin/examples/HelloWorldWebSockets.kt
index 77f4564c..2e8e70b0 100644
--- a/src/test/kotlin/io/javalin/examples/HelloWorldWebSockets.kt
+++ b/src/test/kotlin/io/javalin/examples/HelloWorldWebSockets.kt
@@ -11,8 +11,7 @@ import io.javalin.Javalin
 // WebSockets also work with ssl,
 // see HelloWorldSecure for how to set that up
 fun main(args: Array<String>) {
-    Javalin.create().apply {
-        enableDevLogging()
+    Javalin.create { it.enableDevLogging() }.apply {
         ws("/websocket") { ws ->
             ws.onConnect { ctx ->
                 println("Connection established")