From 707400a8b2c26858cdb784ecd8c14e1113d06322 Mon Sep 17 00:00:00 2001 From: James Maes Date: Sat, 14 Jun 2025 16:07:51 -0500 Subject: [PATCH] Added support for loading static files from the filesystem as as from jars (based on a system property) --- qqq-middleware-javalin/pom.xml | 12 ++++ .../SimpleFileSystemDirectoryRouter.java | 68 ++++++++++++++----- ...ApplicationJavalinServerClasspathTest.java | 61 +++++++++++++++++ .../QApplicationJavalinServerTest.java | 40 ++++++++--- 4 files changed, 154 insertions(+), 27 deletions(-) create mode 100644 qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QApplicationJavalinServerClasspathTest.java diff --git a/qqq-middleware-javalin/pom.xml b/qqq-middleware-javalin/pom.xml index dc94f1ae..43ff6aef 100644 --- a/qqq-middleware-javalin/pom.xml +++ b/qqq-middleware-javalin/pom.xml @@ -122,6 +122,18 @@ src/main/java + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.3 + + + + ${project.basedir}/src/test/resources/static-site.jar + + + + org.apache.maven.plugins maven-compiler-plugin diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/routeproviders/SimpleFileSystemDirectoryRouter.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/routeproviders/SimpleFileSystemDirectoryRouter.java index c6504be1..89e424e2 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/routeproviders/SimpleFileSystemDirectoryRouter.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/routeproviders/SimpleFileSystemDirectoryRouter.java @@ -49,16 +49,34 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair; public class SimpleFileSystemDirectoryRouter implements QJavalinRouteProviderInterface { private static final QLogger LOG = QLogger.getLogger(SimpleFileSystemDirectoryRouter.class); + public static boolean loadStaticFilesFromJar = false; + + static + { + /////////////////////////////////////////////////////////////////////////////////////////////////////// + // read the property to see if we should load static files from the jar file or from the file system // + // Javan only supports loading via one method per path, so its a choice of one or the other... // + /////////////////////////////////////////////////////////////////////////////////////////////////////// + try + { + String propertyName = "qqq.javalin.enableStaticFilesFromJar"; // TODO: make a more general way to handle properties like this system-wide via a central config class + String propertyValue = System.getProperty(propertyName, ""); + if(propertyValue.equals("true")) + { + loadStaticFilesFromJar = true; + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } private final String hostedPath; private final String fileSystemPath; - private QCodeReference routeAuthenticator; - private QInstance qInstance; - - /******************************************************************************* ** Constructor ** @@ -98,25 +116,41 @@ public class SimpleFileSystemDirectoryRouter implements QJavalinRouteProviderInt ***************************************************************************/ private void handleJavalinStaticFileConfig(StaticFileConfig staticFileConfig) { - URL resource = getClass().getClassLoader().getResource(fileSystemPath); - if(resource == null) - { - String message = "Could not find file system path: " + fileSystemPath; - if(fileSystemPath.startsWith("/") && getClass().getClassLoader().getResource(fileSystemPath.replaceFirst("^/+", "")) != null) - { - message += ". For non-absolute paths, do not prefix with a leading slash."; - } - throw new RuntimeException(message); - } if(!hostedPath.startsWith("/")) { LOG.warn("hostedPath [" + hostedPath + "] should probably start with a leading slash..."); } - staticFileConfig.directory = resource.getFile(); - staticFileConfig.hostedPath = hostedPath; - staticFileConfig.location = Location.EXTERNAL; + /// ///////////////////////////////////////////////////////////////////////////////////// + // Handle loading static files from the jar OR the filesystem based on system property // + /// ///////////////////////////////////////////////////////////////////////////////////// + if(SimpleFileSystemDirectoryRouter.loadStaticFilesFromJar) + { + staticFileConfig.directory = fileSystemPath; + staticFileConfig.hostedPath = hostedPath; + staticFileConfig.location = Location.CLASSPATH; + LOG.info("Static File Config : hostedPath [" + hostedPath + "] : directory [" + staticFileConfig.directory + "] : location [CLASSPATH]"); + } + else + { + URL resource = getClass().getClassLoader().getResource(fileSystemPath); + if(resource == null) + { + String message = "Could not find file system path: " + fileSystemPath; + if(fileSystemPath.startsWith("/") && getClass().getClassLoader().getResource(fileSystemPath.replaceFirst("^/+", "")) != null) + { + message += ". For non-absolute paths, do not prefix with a leading slash."; + } + throw new RuntimeException(message); + } + + staticFileConfig.directory = resource.getFile(); + staticFileConfig.hostedPath = hostedPath; + staticFileConfig.location = Location.EXTERNAL; + LOG.info("Static File Config : hostedPath [" + hostedPath + "] : directory [" + staticFileConfig.directory + "] : location [EXTERNAL]"); + } + } diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QApplicationJavalinServerClasspathTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QApplicationJavalinServerClasspathTest.java new file mode 100644 index 00000000..f75b99d1 --- /dev/null +++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QApplicationJavalinServerClasspathTest.java @@ -0,0 +1,61 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.middleware.javalin; + + +import com.kingsrook.qqq.middleware.javalin.routeproviders.SimpleFileSystemDirectoryRouter; +import kong.unirest.HttpResponse; +import kong.unirest.Unirest; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +/******************************************************************************* + ** Unit test for QApplicationJavalinServer with static files served from classpath + *******************************************************************************/ +class QApplicationJavalinServerClasspathTest +{ + private static final int PORT = 6265; + private QApplicationJavalinServer javalinServer; + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testStaticRouterFilesFromClasspath() throws Exception + { + System.setProperty("qqq.javalin.enableStaticFilesFromJar", "true"); + + javalinServer = new QApplicationJavalinServer(new QApplicationJavalinServerTest.TestApplication()) + .withServeFrontendMaterialDashboard(false) + .withPort(PORT) + .withAdditionalRouteProvider(new SimpleFileSystemDirectoryRouter("/statically-served-from-jar", "static-site-from-jar/")); + + javalinServer.start(); + + Unirest.config().setDefaultResponseEncoding("UTF-8"); + HttpResponse response = Unirest.get("http://localhost:" + PORT + "/statically-served-from-jar/foo-in-jar.html").asString(); + assertEquals("Foo in a Jar!\n", response.getBody()); + } +} \ No newline at end of file diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QApplicationJavalinServerTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QApplicationJavalinServerTest.java index a6bf4085..1d3c8e72 100644 --- a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QApplicationJavalinServerTest.java +++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QApplicationJavalinServerTest.java @@ -52,6 +52,16 @@ class QApplicationJavalinServerTest + /*************************************************************************** + ** + ***************************************************************************/ + private static AbstractQQQApplication getQqqApplication() + { + return new TestApplication(); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -196,6 +206,26 @@ class QApplicationJavalinServerTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testStaticRouterFilesFromExternal() throws Exception + { + System.setProperty("qqq.javalin.enableStaticFilesFromJar", "false"); + + javalinServer = new QApplicationJavalinServer(getQqqApplication()) + .withServeFrontendMaterialDashboard(false) + .withPort(PORT); + javalinServer.start(); + + Unirest.config().setDefaultResponseEncoding("UTF-8"); + HttpResponse response = Unirest.get("http://localhost:" + PORT + "/statically-served/foo.html").asString(); + assertEquals("Foo? Bar!", response.getBody()); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -296,16 +326,6 @@ class QApplicationJavalinServerTest - /*************************************************************************** - ** - ***************************************************************************/ - private static AbstractQQQApplication getQqqApplication() - { - return new TestApplication(); - } - - - /*************************************************************************** ** ***************************************************************************/