Repository: tipsy/javalin
Pull request #529 (2019-03-31 14:03:04)

[events] Create events-attacher class to handle public api for events
```
app.on.serverStarting {  }
app.on.serverStarted {  }
app.on.serverStopping {  }
app.on.serverStopping {  }
app.on.serverStopping {  }
app.on.serverStopped {  }
app.on.handlerAdded { handlerMetaInfo -> }
```


Patch:
diff --git a/src/main/java/io/javalin/Javalin.java b/src/main/java/io/javalin/Javalin.java
index 805de121..66f6b72f 100644
--- a/src/main/java/io/javalin/Javalin.java
+++ b/src/main/java/io/javalin/Javalin.java
@@ -9,8 +9,11 @@ package io.javalin;
 
 import io.javalin.apibuilder.ApiBuilder;
 import io.javalin.apibuilder.EndpointGroup;
+import io.javalin.core.EventAttacher;
 import io.javalin.core.EventManager;
+import io.javalin.core.HandlerMetaInfo;
 import io.javalin.core.HandlerType;
+import io.javalin.core.JavalinEvent;
 import io.javalin.core.JavalinServer;
 import io.javalin.core.JavalinServlet;
 import io.javalin.core.JettyUtil;
@@ -49,7 +52,9 @@ public class Javalin {
 
     protected boolean showStartupBanner = true;
 
-    protected EventManager eventManager = new EventManager();
+    protected EventManager eventManager = new EventManager(this);
+    public EventAttacher on = eventManager.getEventAttacher();
+
     protected Map<Class<?>, Object> appAttributes = new HashMap<>();
 
     private JavalinServer server = new JavalinServer();
@@ -439,26 +444,6 @@ public class Javalin {
         return this;
     }
 
-    /**
-     * Adds a lifecycle event listener.
-     * The method must be called before {@link Javalin#start()}.
-     *
-     * @see <a href="https://javalin.io/documentation#lifecycle-events">Events in docs</a>
-     */
-    public Javalin on(@NotNull JavalinEvent javalinEvent, @NotNull Runnable callback) {
-        ensureActionIsPerformedBeforeServerStart("Event-mapping");
-        eventManager.getCallbackMap().get(javalinEvent).add(callback);
-        return this;
-    }
-
-    /**
-     * Adds a handler-added event listener which exposes meta-info about the added handler
-     */
-    public Javalin onHandlerAdded(Consumer<HandlerMetaInfo> callback) {
-        eventManager.setHandlerAddedCallback(callback);
-        return this;
-    }
-
     /**
      * Adds an error mapper to the instance.
      * Useful for turning error-codes (404, 500) into standardized messages/pages
diff --git a/src/main/java/io/javalin/_DataClasses.kt b/src/main/java/io/javalin/_DataClasses.kt
index 3344abb1..bbcc72b7 100644
--- a/src/main/java/io/javalin/_DataClasses.kt
+++ b/src/main/java/io/javalin/_DataClasses.kt
@@ -6,8 +6,6 @@
 
 package io.javalin
 
-import io.javalin.core.HandlerType
-import io.javalin.security.Role
 import java.io.InputStream
 
 /**
@@ -27,17 +25,3 @@ data class UploadedFile(val contentType: String, val content: InputStream, val n
  * @see Context.basicAuthCredentials
  */
 data class BasicAuthCredentials(val username: String, val password: String)
-
-/**
- * Server lifecycle events
- * @see Javalin.on
- */
-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>)
diff --git a/src/main/java/io/javalin/core/EventAttacher.java b/src/main/java/io/javalin/core/EventAttacher.java
new file mode 100644
index 00000000..abd94d61
--- /dev/null
+++ b/src/main/java/io/javalin/core/EventAttacher.java
@@ -0,0 +1,39 @@
+/*
+ * Javalin - https://javalin.io
+ * Copyright 2017 David Åse
+ * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
+ */
+
+package io.javalin.core;
+
+import io.javalin.Javalin;
+import java.util.function.Consumer;
+import org.jetbrains.annotations.NotNull;
+
+public class EventAttacher {
+
+    private EventManager eventManager;
+
+    public EventAttacher(EventManager eventManager) {
+        this.eventManager = eventManager;
+    }
+
+    // @formatter:off
+    public Javalin serverStarting(@NotNull Runnable callback)    { return addLifecycleEvent(JavalinEvent.SERVER_STARTING,     callback); }
+    public Javalin serverStarted(@NotNull Runnable callback)     { return addLifecycleEvent(JavalinEvent.SERVER_STARTED,      callback); }
+    public Javalin serverStartFailed(@NotNull Runnable callback) { return addLifecycleEvent(JavalinEvent.SERVER_START_FAILED, callback); }
+    public Javalin serverStopping(@NotNull Runnable callback)    { return addLifecycleEvent(JavalinEvent.SERVER_STOPPING,     callback); }
+    public Javalin serverStopped(@NotNull Runnable callback)     { return addLifecycleEvent(JavalinEvent.SERVER_STOPPED,      callback); }
+    // @formatter:on
+
+    public Javalin handlerAdded(@NotNull Consumer<HandlerMetaInfo> callback) {
+        this.eventManager.setHandlerAddedCallback(callback);
+        return eventManager.getParentJavalin();
+    }
+
+    private Javalin addLifecycleEvent(@NotNull JavalinEvent event, @NotNull Runnable callback) {
+        eventManager.getCallbackMap().get(event).add(callback);
+        return eventManager.getParentJavalin();
+    }
+
+}
diff --git a/src/main/java/io/javalin/core/EventManager.kt b/src/main/java/io/javalin/core/EventManager.kt
index 8cb5c398..758f438d 100644
--- a/src/main/java/io/javalin/core/EventManager.kt
+++ b/src/main/java/io/javalin/core/EventManager.kt
@@ -6,15 +6,18 @@
 
 package io.javalin.core
 
-import io.javalin.HandlerMetaInfo
-import io.javalin.JavalinEvent
+import io.javalin.Javalin
+import io.javalin.security.Role
 import java.util.*
 import java.util.function.Consumer
 
-class EventManager {
+class EventManager(val parentJavalin: Javalin) {
+    val eventAttacher = EventAttacher(this)
     val callbackMap = JavalinEvent.values().associate { it to ArrayList<Runnable>() }
     fun fireEvent(javalinEvent: JavalinEvent) = callbackMap[javalinEvent]!!.forEach { callback -> callback.run() }
-
-    var handlerAddedCallback: Consumer<HandlerMetaInfo>? = null
+    var handlerAddedCallback: (Consumer<HandlerMetaInfo>)? = null
     fun fireHandlerAddedEvent(handlerMetaInfo: HandlerMetaInfo) = handlerAddedCallback.apply { this?.accept(handlerMetaInfo) }
 }
+
+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>)
diff --git a/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt b/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
index abf0105b..f18a7f86 100644
--- a/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
+++ b/src/main/java/io/javalin/core/util/RouteOverviewUtil.kt
@@ -8,15 +8,15 @@ package io.javalin.core.util
 
 import io.javalin.Context
 import io.javalin.Handler
-import io.javalin.HandlerMetaInfo
 import io.javalin.Javalin
+import io.javalin.core.HandlerMetaInfo
 
 class RouteOverviewRenderer(val app: Javalin) : Handler {
 
     val handlerMetaInfo = mutableListOf<HandlerMetaInfo>()
 
     init {
-        app.onHandlerAdded { handlerMetaInfo.add(it) }
+        app.on.handlerAdded { handlerMetaInfo.add(it) }
     }
 
     override fun handle(ctx: Context) {


Test patch:
diff --git a/src/test/java/io/javalin/TestLifecycleEvents.kt b/src/test/java/io/javalin/TestLifecycleEvents.kt
index d519995a..935bc532 100644
--- a/src/test/java/io/javalin/TestLifecycleEvents.kt
+++ b/src/test/java/io/javalin/TestLifecycleEvents.kt
@@ -7,6 +7,7 @@
 
 package io.javalin
 
+import io.javalin.util.TestUtil
 import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 
@@ -16,14 +17,22 @@ class TestLifecycleEvents {
     fun `life cycle events work`() {
         var log = ""
         Javalin.create().apply {
-            on(JavalinEvent.SERVER_STARTING) { log += "Starting" }
-            on(JavalinEvent.SERVER_STARTED) { log += "Started" }
-            on(JavalinEvent.SERVER_STOPPING) { log += "Stopping" }
-            on(JavalinEvent.SERVER_STOPPING) { log += "Stopping" }
-            on(JavalinEvent.SERVER_STOPPING) { log += "Stopping" }
-            on(JavalinEvent.SERVER_STOPPED) { log += "Stopped" }
+            on.serverStarting { log += "Starting" }
+            on.serverStarted { log += "Started" }
+            on.serverStopping { log += "Stopping" }
+            on.serverStopping { log += "Stopping" }
+            on.serverStopping { log += "Stopping" }
+            on.serverStopped { log += "Stopped" }
         }.start(0).stop()
         assertThat(log).isEqualTo("StartingStartedStoppingStoppingStoppingStopped")
     }
 
+    @Test
+    fun `handlerAdded event works`() = TestUtil.test { app, http ->
+        var log = ""
+        app.on.handlerAdded { handlerMetaInfo -> log += handlerMetaInfo.path }
+        app.get("/test-path") {}
+        assertThat(log).isEqualTo("/test-path")
+    }
+
 }