This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 162d68c85c7 embedded HTTP server can now be used for static pages like 
a html web app (#15399)
162d68c85c7 is described below

commit 162d68c85c7ac62d6a28019a0116c53184cf356b
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 2 17:07:51 2024 +0200

    embedded HTTP server can now be used for static pages like a html web app 
(#15399)
    
    CAMEL-21110: Add support for serving static content in embedded main HTTP 
server
---
 .../main/camel-main-configuration-metadata.json    |  2 +
 .../http/main/DefaultMainHttpServerFactory.java    |  2 +
 .../platform/http/main/MainHttpServer.java         | 91 ++++++++++++++++++++++
 ...ttpServerConfigurationPropertiesConfigurer.java | 12 +++
 .../camel-main-configuration-metadata.json         |  2 +
 core/camel-main/src/main/docs/main.adoc            |  4 +-
 .../main/HttpServerConfigurationProperties.java    | 45 +++++++++++
 7 files changed, 157 insertions(+), 1 deletion(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
index df98648028e..a4e6e092707 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/main/camel-main-configuration-metadata.json
@@ -274,6 +274,8 @@
     { "name": "camel.server.metricsEnabled", "description": "Whether to enable 
metrics. If enabled then you can access metrics on context-path: \/q\/metrics", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.server.path", "description": "Context-path to use for 
embedded HTTP server", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/" },
     { "name": "camel.server.port", "description": "Port to use for binding 
embedded HTTP server", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "int", "defaultValue": 8080 },
+    { "name": "camel.server.staticContextPath", "description": "The 
context-path to use for serving static content. By default, the root path is 
used. And if there is an index.html page then this is automatically loaded.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "string", "javaType": "java.lang.String", "defaultValue": "\/" },
+    { "name": "camel.server.staticEnabled", "description": "Whether serving 
static files is enabled. If enabled then Camel can host html\/js and other web 
files that makes it possible to include small web applications.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.server.uploadEnabled", "description": "Whether to enable 
file upload via HTTP (not intended for production use). This functionality is 
for development to be able to reload Camel routes and code with source changes 
(if reload is enabled). If enabled then you can upload\/delete files via HTTP 
PUT\/DELETE on context-path: \/q\/upload\/{name}. You must also configure the 
uploadSourceDir option.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "t [...]
     { "name": "camel.server.uploadSourceDir", "description": "Source directory 
when upload is enabled.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String" },
     { "name": "camel.server.useGlobalSslContextParameters", "description": 
"Whether to use global SSL configuration for securing the embedded HTTP 
server.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": "false" },
diff --git 
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/DefaultMainHttpServerFactory.java
 
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/DefaultMainHttpServerFactory.java
index d0de015667a..e1da6e422de 100644
--- 
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/DefaultMainHttpServerFactory.java
+++ 
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/DefaultMainHttpServerFactory.java
@@ -55,6 +55,8 @@ public class DefaultMainHttpServerFactory implements 
CamelContextAware, MainHttp
         }
         
server.setUseGlobalSslContextParameters(configuration.isUseGlobalSslContextParameters());
         server.setInfoEnabled(configuration.isInfoEnabled());
+        server.setStaticEnabled(configuration.isStaticEnabled());
+        server.setStaticContextPath(configuration.getStaticContextPath());
         server.setDevConsoleEnabled(configuration.isDevConsoleEnabled());
         server.setHealthCheckEnabled(configuration.isHealthCheckEnabled());
         server.setJolokiaEnabled(configuration.isJolokiaEnabled());
diff --git 
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/MainHttpServer.java
 
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/MainHttpServer.java
index 94a03fcf682..c2d3169462d 100644
--- 
a/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/MainHttpServer.java
+++ 
b/components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/MainHttpServer.java
@@ -17,8 +17,10 @@
 package org.apache.camel.component.platform.http.main;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 import java.util.ArrayList;
@@ -37,6 +39,7 @@ import java.util.stream.Collectors;
 
 import io.vertx.core.Handler;
 import io.vertx.core.http.HttpMethod;
+import io.vertx.core.http.impl.MimeMapping;
 import io.vertx.ext.web.RequestBody;
 import io.vertx.ext.web.Route;
 import io.vertx.ext.web.RoutingContext;
@@ -102,6 +105,8 @@ public class MainHttpServer extends ServiceSupport 
implements CamelContextAware,
 
     private VertxPlatformHttpServerConfiguration configuration = new 
VertxPlatformHttpServerConfiguration();
     private boolean infoEnabled;
+    private boolean staticEnabled;
+    private String staticContextPath;
     private boolean devConsoleEnabled;
     private boolean healthCheckEnabled;
     private boolean jolokiaEnabled;
@@ -137,6 +142,29 @@ public class MainHttpServer extends ServiceSupport 
implements CamelContextAware,
         this.infoEnabled = infoEnabled;
     }
 
+    @ManagedAttribute(description = "Whether serving static content is enabled 
(such as html pages)")
+    public boolean isStaticEnabled() {
+        return staticEnabled;
+    }
+
+    public void setStaticEnabled(boolean staticEnabled) {
+        this.staticEnabled = staticEnabled;
+    }
+
+    @ManagedAttribute(description = "The context-path for serving static 
content")
+    public String getStaticContextPath() {
+        return staticContextPath;
+    }
+
+    public void setStaticContextPath(String staticContextPath) {
+        this.staticContextPath = staticContextPath;
+    }
+
+    @ManagedAttribute(description = "Whether serving static content is enabled 
(such as html pages)")
+    public boolean isStaticFilePattern() {
+        return staticEnabled;
+    }
+
     @ManagedAttribute(description = "Whether dev console is enabled (/q/dev)")
     public boolean isDevConsoleEnabled() {
         return devConsoleEnabled;
@@ -336,6 +364,9 @@ public class MainHttpServer extends ServiceSupport 
implements CamelContextAware,
         if (infoEnabled) {
             setupInfo();
         }
+        if (staticEnabled) {
+            setupStatic();
+        }
         if (devConsoleEnabled) {
             setupDevConsole();
         }
@@ -437,6 +468,66 @@ public class MainHttpServer extends ServiceSupport 
implements CamelContextAware,
         });
     }
 
+    protected void setupStatic() {
+        String path = staticContextPath;
+        if (!path.endsWith("*")) {
+            path = path + "*";
+        }
+        final Route web = router.route(path);
+        web.produces("*");
+        web.consumes("*");
+        web.order(Integer.MAX_VALUE); // run this last so all other are served 
first
+
+        Handler<RoutingContext> handler = new Handler<RoutingContext>() {
+            @Override
+            public void handle(RoutingContext ctx) {
+                String u = ctx.normalizedPath();
+                if (u.isBlank() || u.endsWith("/") || u.equals("index.html")) {
+                    u = "index.html";
+                } else {
+                    u = FileUtil.stripLeadingSeparator(u);
+                }
+
+                InputStream is = null;
+                File f = new File(u);
+                if (f.exists()) {
+                    // load directly from file system first
+                    try {
+                        is = new FileInputStream(f);
+                    } catch (Exception e) {
+                        // ignore
+                    }
+                } else {
+                    is = 
camelContext.getClassResolver().loadResourceAsStream(u);
+                }
+                if (is != null) {
+                    String mime = 
MimeMapping.getMimeTypeForFilename(f.getName());
+                    if (mime != null) {
+                        ctx.response().putHeader("content-type", mime);
+                    }
+                    String text = null;
+                    try {
+                        text = IOHelper.loadText(is);
+                    } catch (Exception e) {
+                        // ignore
+                    } finally {
+                        IOHelper.close(is);
+                    }
+                    ctx.response().setStatusCode(200);
+                    ctx.end(text);
+                } else {
+                    ctx.response().setStatusCode(404);
+                    ctx.end();
+                }
+            }
+        };
+
+        // use blocking handler as the task can take longer time to complete
+        web.handler(new BlockingHandlerDecorator(handler, true));
+
+        platformHttpComponent.addHttpEndpoint(staticContextPath, null, null, 
null, null);
+    }
+
     protected void setupInfo() {
         final Route info = router.route("/q/info");
         info.method(HttpMethod.GET);
diff --git 
a/core/camel-main/src/generated/java/org/apache/camel/main/HttpServerConfigurationPropertiesConfigurer.java
 
b/core/camel-main/src/generated/java/org/apache/camel/main/HttpServerConfigurationPropertiesConfigurer.java
index d90376adc33..af40d20036f 100644
--- 
a/core/camel-main/src/generated/java/org/apache/camel/main/HttpServerConfigurationPropertiesConfigurer.java
+++ 
b/core/camel-main/src/generated/java/org/apache/camel/main/HttpServerConfigurationPropertiesConfigurer.java
@@ -53,6 +53,10 @@ public class HttpServerConfigurationPropertiesConfigurer 
extends org.apache.came
         case "metricsEnabled": target.setMetricsEnabled(property(camelContext, 
boolean.class, value)); return true;
         case "path": target.setPath(property(camelContext, 
java.lang.String.class, value)); return true;
         case "port": target.setPort(property(camelContext, int.class, value)); 
return true;
+        case "staticcontextpath":
+        case "staticContextPath": 
target.setStaticContextPath(property(camelContext, java.lang.String.class, 
value)); return true;
+        case "staticenabled":
+        case "staticEnabled": target.setStaticEnabled(property(camelContext, 
boolean.class, value)); return true;
         case "uploadenabled":
         case "uploadEnabled": target.setUploadEnabled(property(camelContext, 
boolean.class, value)); return true;
         case "uploadsourcedir":
@@ -96,6 +100,10 @@ public class HttpServerConfigurationPropertiesConfigurer 
extends org.apache.came
         case "metricsEnabled": return boolean.class;
         case "path": return java.lang.String.class;
         case "port": return int.class;
+        case "staticcontextpath":
+        case "staticContextPath": return java.lang.String.class;
+        case "staticenabled":
+        case "staticEnabled": return boolean.class;
         case "uploadenabled":
         case "uploadEnabled": return boolean.class;
         case "uploadsourcedir":
@@ -140,6 +148,10 @@ public class HttpServerConfigurationPropertiesConfigurer 
extends org.apache.came
         case "metricsEnabled": return target.isMetricsEnabled();
         case "path": return target.getPath();
         case "port": return target.getPort();
+        case "staticcontextpath":
+        case "staticContextPath": return target.getStaticContextPath();
+        case "staticenabled":
+        case "staticEnabled": return target.isStaticEnabled();
         case "uploadenabled":
         case "uploadEnabled": return target.isUploadEnabled();
         case "uploadsourcedir":
diff --git 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index df98648028e..a4e6e092707 100644
--- 
a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ 
b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -274,6 +274,8 @@
     { "name": "camel.server.metricsEnabled", "description": "Whether to enable 
metrics. If enabled then you can access metrics on context-path: \/q\/metrics", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.server.path", "description": "Context-path to use for 
embedded HTTP server", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String", "defaultValue": "\/" },
     { "name": "camel.server.port", "description": "Port to use for binding 
embedded HTTP server", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "integer", 
"javaType": "int", "defaultValue": 8080 },
+    { "name": "camel.server.staticContextPath", "description": "The 
context-path to use for serving static content. By default, the root path is 
used. And if there is an index.html page then this is automatically loaded.", 
"sourceType": "org.apache.camel.main.HttpServerConfigurationProperties", 
"type": "string", "javaType": "java.lang.String", "defaultValue": "\/" },
+    { "name": "camel.server.staticEnabled", "description": "Whether serving 
static files is enabled. If enabled then Camel can host html\/js and other web 
files that makes it possible to include small web applications.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.server.uploadEnabled", "description": "Whether to enable 
file upload via HTTP (not intended for production use). This functionality is 
for development to be able to reload Camel routes and code with source changes 
(if reload is enabled). If enabled then you can upload\/delete files via HTTP 
PUT\/DELETE on context-path: \/q\/upload\/{name}. You must also configure the 
uploadSourceDir option.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "t [...]
     { "name": "camel.server.uploadSourceDir", "description": "Source directory 
when upload is enabled.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "string", 
"javaType": "java.lang.String" },
     { "name": "camel.server.useGlobalSslContextParameters", "description": 
"Whether to use global SSL configuration for securing the embedded HTTP 
server.", "sourceType": 
"org.apache.camel.main.HttpServerConfigurationProperties", "type": "boolean", 
"javaType": "boolean", "defaultValue": "false" },
diff --git a/core/camel-main/src/main/docs/main.adoc 
b/core/camel-main/src/main/docs/main.adoc
index df6c0047c07..84338b63325 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -170,7 +170,7 @@ The camel.routecontroller supports 12 options, which are 
listed below.
 
 
 === Camel Embedded HTTP Server (only for standalone; not Spring Boot or 
Quarkus) configurations
-The camel.server supports 20 options, which are listed below.
+The camel.server supports 22 options, which are listed below.
 
 [width="100%",cols="2,5,^1,2",options="header"]
 |===
@@ -192,6 +192,8 @@ The camel.server supports 20 options, which are listed 
below.
 | *camel.server.metricsEnabled* | Whether to enable metrics. If enabled then 
you can access metrics on context-path: /q/metrics | false | boolean
 | *camel.server.path* | Context-path to use for embedded HTTP server | / | 
String
 | *camel.server.port* | Port to use for binding embedded HTTP server | 8080 | 
int
+| *camel.server.staticContextPath* | The context-path to use for serving 
static content. By default, the root path is used. And if there is an 
index.html page then this is automatically loaded. | / | String
+| *camel.server.staticEnabled* | Whether serving static files is enabled. If 
enabled then Camel can host html/js and other web files that makes it possible 
to include small web applications. | false | boolean
 | *camel.server.uploadEnabled* | Whether to enable file upload via HTTP (not 
intended for production use). This functionality is for development to be able 
to reload Camel routes and code with source changes (if reload is enabled). If 
enabled then you can upload/delete files via HTTP PUT/DELETE on context-path: 
/q/upload/\{name}. You must also configure the uploadSourceDir option. | false 
| boolean
 | *camel.server.uploadSourceDir* | Source directory when upload is enabled. |  
| String
 | *camel.server.useGlobalSsl{zwsp}ContextParameters* | Whether to use global 
SSL configuration for securing the embedded HTTP server. | false | boolean
diff --git 
a/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
 
b/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
index c5dd5ec54f9..96d5e0128b6 100644
--- 
a/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
+++ 
b/core/camel-main/src/main/java/org/apache/camel/main/HttpServerConfigurationProperties.java
@@ -40,6 +40,9 @@ public class HttpServerConfigurationProperties implements 
BootstrapCloseable {
     private boolean useGlobalSslContextParameters;
 
     private boolean infoEnabled;
+    private boolean staticEnabled;
+    @Metadata(defaultValue = "/")
+    private String staticContextPath = "/";
     private boolean devConsoleEnabled;
     private boolean healthCheckEnabled;
     private boolean jolokiaEnabled;
@@ -151,6 +154,30 @@ public class HttpServerConfigurationProperties implements 
BootstrapCloseable {
         this.infoEnabled = infoEnabled;
     }
 
+    public boolean isStaticEnabled() {
+        return staticEnabled;
+    }
+
+    /**
+     * Whether serving static files is enabled. If enabled then Camel can host 
html/js and other web files that makes it
+     * possible to include small web applications.
+     */
+    public void setStaticEnabled(boolean staticEnabled) {
+        this.staticEnabled = staticEnabled;
+    }
+
+    public String getStaticContextPath() {
+        return staticContextPath;
+    }
+
+    /**
+     * The context-path to use for serving static content. By default, the 
root path is used. And if there is an
+     * index.html page then this is automatically loaded.
+     */
+    public void setStaticContextPath(String staticContextPath) {
+        this.staticContextPath = staticContextPath;
+    }
+
     public boolean isDevConsoleEnabled() {
         return devConsoleEnabled;
     }
@@ -360,6 +387,24 @@ public class HttpServerConfigurationProperties implements 
BootstrapCloseable {
         return this;
     }
 
+    /**
+     * Whether serving static files is enabled. If enabled then Camel can host 
html/js and other web files that makes it
+     * possible to include small web applications.
+     */
+    public HttpServerConfigurationProperties withStaticEnabled(boolean 
staticEnabled) {
+        this.staticEnabled = staticEnabled;
+        return this;
+    }
+
+    /**
+     * The context-path to use for serving static content. By default, the 
root path is used. And if there is an
+     * index.html page then this is automatically loaded.
+     */
+    public HttpServerConfigurationProperties withStaticContextPath(String 
staticContextPath) {
+        this.staticContextPath = staticContextPath;
+        return this;
+    }
+
     /**
      * Whether to enable developer console (not intended for production use). 
Dev console must also be enabled on
      * CamelContext. For example by setting camel.context.dev-console=true in 
application.properties, or via code

Reply via email to