diff --git a/jpro-routing/core-test/src/test/scala/one/jpro/platform/routing/sessionmanager/TestSessionManager.scala b/jpro-routing/core-test/src/test/scala/one/jpro/platform/routing/sessionmanager/TestSessionManager.scala index a61e6d5c..75195e84 100644 --- a/jpro-routing/core-test/src/test/scala/one/jpro/platform/routing/sessionmanager/TestSessionManager.scala +++ b/jpro-routing/core-test/src/test/scala/one/jpro/platform/routing/sessionmanager/TestSessionManager.scala @@ -1,7 +1,7 @@ package one.jpro.platform.routing.sessionmanager import javafx.scene.control.Label -import one.jpro.platform.routing.{Response, Route, RouteApp} +import one.jpro.platform.routing.{Filters, Response, Route, RouteApp} import org.junit.jupiter.api.Test import simplefx.core._ @@ -36,4 +36,53 @@ class TestSessionManager { assert(url == "/test/test2") } } + + @Test + def testErrorPage(): Unit = { + val route = Route.empty() + .and(Route.get("/",r => Response.node(new Label("Empty")))) + .and(Route.get("/error", r => throw new Exception("Error"))) + .and(Route.get("/error2", r => Response.error(new Exception("Error2")))) + .filter(Filters.errorPage()) + + val app = new RouteApp { + override def createRoute(): Route = route + } + val stage = inFX(new javafx.stage.Stage()) + inFX(app.startFuture(stage)).future.await + + val res1 = inFX(app.getSessionManager().gotoURL("/error").future).await + inFX { + val view = app.getSessionManager().view + assert(view.realContent.asInstanceOf[Label].getText.contains("Error"), view.realContent.asInstanceOf[Label].getText) + } + + val res2 = inFX(app.getSessionManager().gotoURL("/error2").future).await + inFX { + val view = app.getSessionManager().view + assert(view.realContent.asInstanceOf[Label].getText.contains("Error2"), view.realContent.asInstanceOf[Label].getText) + } + } + + @Test + def testNotFoundPage(): Unit = { + val route = Route.empty() + .and(Route.get("/",r => Response.node(new Label("Empty")))) + .filter(Filters.notFoundPage()) + .filter(Filters.notFoundPage(r => Response.node(new Label("Not Found: " + r.getPath())))) + + + val app = new RouteApp { + override def createRoute(): Route = route + } + val stage = inFX(new javafx.stage.Stage()) + inFX(app.startFuture(stage)).future.await + + val res = inFX(app.getSessionManager().gotoURL("/notfound").future).await + inFX { + val view = app.getSessionManager().view + assert(view.realContent.asInstanceOf[Label].getText.contains("Not Found"), view.realContent.asInstanceOf[Label].getText) + println("Label Text: " + view.realContent.asInstanceOf[Label].getText) + } + } } diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/Filters.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/Filters.scala index 4ba8f94f..3e94920a 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/Filters.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/Filters.scala @@ -1,6 +1,9 @@ package one.jpro.platform.routing +import javafx.scene.control.Label import simplefx.all +import java.util.function.Function +import java.util.function.BiFunction object Filters { def FullscreenFilter(fullscreenValue: Boolean): Filter = { route => { request => @@ -20,4 +23,26 @@ object Filters { } } + def errorPage(): Filter = errorPage((request, ex) => Response.node(new Label("Error: " + ex.getMessage))) + def errorPage(biFunction: BiFunction[Request, Throwable, Response]): Filter = { + route => { request => + try { + val r = route.apply(request) + Response.fromFuture(r.future.map(x => Response.fromResult(x)).exceptionally { ex => + biFunction.apply(request, ex) + }) + } catch { + case ex: Throwable => + biFunction.apply(request, ex) + } + } + } + + def notFoundPage(): Filter = { + notFoundPage((request) => Response.node(new Label("Not Found: " + request.getPath()))) + } + def notFoundPage(function: Route): Filter = { route => + route.and(function) + } + } diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/RouteApp.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/RouteApp.scala index bb6f857f..5ceb8e10 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/RouteApp.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/RouteApp.scala @@ -6,6 +6,9 @@ import javafx.scene.Scene import javafx.scene.layout.StackPane import javafx.stage.{Stage, StageStyle} import one.jpro.platform.routing.sessionmanager.SessionManager +import simplefx.core._ +import simplefx.all._ +import simplefx.experimental.FXFuture abstract class RouteApp extends Application { @@ -21,6 +24,9 @@ abstract class RouteApp extends Application { def getWebAPI(): WebAPI = if(WebAPI.isBrowser) WebAPI.getWebAPI(getStage()) else null override def start(stage: Stage): Unit = { + startFuture(stage) + } + def startFuture(stage: Stage): Response = { _stage = stage stage.initStyle(stageStyle()) routeNode = new RouteNode(stage) diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/RouteNode.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/RouteNode.scala index 3840649d..fd4901cd 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/RouteNode.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/RouteNode.scala @@ -36,7 +36,7 @@ class RouteNode(stage: Stage, route: Route) extends StackPane { THIS => def setRoute(x: Route): Unit = newRoute = x - def start(sessionManager: SessionManager) = { + def start(sessionManager: SessionManager): Response = { SessionManagerContext.setContext(this, sessionManager) sessionManager.start() } diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManager.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManager.scala index e5b1e48e..1c56836d 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManager.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManager.scala @@ -75,7 +75,7 @@ trait SessionManager { THIS => Request.fromString(url, node) } - def start(): Unit + def start(): Response def markViewCollectable(view: View): Unit = { JMemoryBuddyLive.markCollectable(s"Page url: ${view.url} title: ${view.title}", view.realContent) diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerDesktop.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerDesktop.scala index 7d7abe1b..5316e7bb 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerDesktop.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerDesktop.scala @@ -81,7 +81,7 @@ class SessionManagerDesktop(val webApp: RouteNode) extends SessionManager { THIS } } - def start(): Unit = { + def start(): Response = { gotoURL("/", pushState = true) } } diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerDummy.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerDummy.scala index c957bce4..27d8ff0d 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerDummy.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerDummy.scala @@ -12,5 +12,5 @@ class SessionManagerDummy(val webApp: RouteNode) extends SessionManager { Response.fromResult(x) } - override def start(): Unit = () + override def start(): Response = Response.empty() } diff --git a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerWeb.scala b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerWeb.scala index 12e13839..1e5fdb29 100644 --- a/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerWeb.scala +++ b/jpro-routing/core/src/main/scala/one/jpro/platform/routing/sessionmanager/SessionManagerWeb.scala @@ -86,14 +86,13 @@ class SessionManagerWeb(val webApp: RouteNode, val webAPI: WebAPI) extends Sessi } } - def gotoFullEncodedURL(x: String, pushState: Boolean = true): Unit = { + def gotoFullEncodedURL(x: String, pushState: Boolean = true): Response = { // We no longer decode - we should only process proper URLs // If the URL is not proper, we will get a warning when creating the Request. gotoURL(x, pushState) } - def start(): Unit = { - gotoFullEncodedURL(webAPI.getBrowserURL, false) + def start(): Response = { logger.debug("registering popstate") webAPI.registerJavaFunction("popstatejava", (s: String) => { gotoFullEncodedURL(s.drop(1).dropRight(1).replace("\\\"", "\"")) @@ -102,7 +101,7 @@ class SessionManagerWeb(val webApp: RouteNode, val webAPI: WebAPI) extends Sessi gotoURL(s.drop(1).dropRight(1).replace("\\\"", "\"")) }) - webAPI.executeScript( + webAPI.js().eval( s"""var scheduled = false |window.addEventListener("scroll", function(e) { | if(!scheduled) { @@ -126,7 +125,7 @@ class SessionManagerWeb(val webApp: RouteNode, val webAPI: WebAPI) extends Sessi // that we have to move back to the saved scrollPosition. // we have to check, whether the ws is still alive, shortly after popstate. // we have to save the old scrollY immediately, so we remember it faster, than the safari resets it. - webAPI.executeScript(""" + webAPI.js().eval(""" |window.addEventListener('popstate', function(e) { | window.setTimeout(function(){console.log("popstate called!")},3000); | var scrollY = 0; @@ -142,11 +141,13 @@ class SessionManagerWeb(val webApp: RouteNode, val webAPI: WebAPI) extends Sessi | }, 1); | jpro.popstatejava(location.href); |});""".stripMargin) - webAPI.executeScript( + webAPI.js().eval( // Back off, browser, I got this... """if ('scrollRestoration' in history) { | history.scrollRestoration = 'manual'; |} """.stripMargin) + + gotoFullEncodedURL(webAPI.getBrowserURL, false) } }