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

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git


The following commit(s) were added to refs/heads/master by this push:
     new d2f00b44fa Updated to use HTTP optionally in HealthCheck (#192)
d2f00b44fa is described below

commit d2f00b44fa04bbfc496548a35b6932c9bf0c829e
Author: Amit Kumar Mondal <[email protected]>
AuthorDate: Fri Jul 28 14:43:14 2023 +0200

    Updated to use HTTP optionally in HealthCheck (#192)
    
    * Imported OSGi HTTP Package Optionally
    
    fixes FELIX-6565
    
    * Replaced use of HTTP Service with HTTP Whiteboard
    
    There was no runtime issue to use the old pattern of using `HttpService` to 
register the servlets, but dependency to `HttpService` introduces a mandatory 
requirement to `osgi.service` namespace.
    
    
`osgi.service;filter:="(objectClass=org.osgi.service.http.HttpService)";effective:=active`
    
    This requirement doesn't play any role in the runtime but it plays a 
significant role in composing systems. If we use bndtools and use this bundle 
as an initial requirement to resolve the dependencies, this will fail as the 
`requirement` needs to be satisfied first. We also use similar mechanism in our 
company to compose systems using `OSGi Resolver` which would not work if this 
bundle comprises such mandatory dependency to `HttpService`. That's why, a 
cleaner approach would be to go  [...]
---
 healthcheck/core/bnd.bnd                           |  5 ++
 .../impl/servlet/HealthCheckExecutorServlet.java   | 77 +++++++++++++++-------
 .../HealthCheckExecutorServletConfiguration.java   |  8 +--
 .../servlet/HealthCheckExecutorServletTest.java    |  6 +-
 4 files changed, 68 insertions(+), 28 deletions(-)

diff --git a/healthcheck/core/bnd.bnd b/healthcheck/core/bnd.bnd
index ceba184353..b35272fe84 100644
--- a/healthcheck/core/bnd.bnd
+++ b/healthcheck/core/bnd.bnd
@@ -9,12 +9,17 @@ Bundle-License: Apache License, Version 2.0
 Bundle-Vendor: The Apache Software Foundation
 
 Import-Package:\
+  org.osgi.service.http*;resolution:=optional,\
   org.quartz*;resolution:=optional,\
   javax.servlet*;resolution:=optional,\
   *
 
 Conditional-Package: org.apache.felix.utils.*
 
+Require-Capability: osgi.implementation; \
+                          
filter:="(&(osgi.implementation=osgi.http)(version>=1.1.0)(!(version>=2.0.0)))";
 \
+                          resolution:=optional
+
 -removeheaders:\
   Include-Resource,\
   Private-Package
diff --git 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServlet.java
 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServlet.java
index be9dac3733..cd4fa8bd1e 100644
--- 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServlet.java
+++ 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServlet.java
@@ -17,15 +17,23 @@
  */
 package org.apache.felix.hc.core.impl.servlet;
 
+import static 
org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT;
+import static 
org.osgi.service.http.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN;
+
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.TreeMap;
 
+import javax.servlet.Servlet;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -38,12 +46,14 @@ import 
org.apache.felix.hc.api.execution.HealthCheckExecutor;
 import org.apache.felix.hc.api.execution.HealthCheckSelector;
 import org.apache.felix.hc.core.impl.executor.CombinedExecutionResult;
 import org.apache.felix.hc.core.impl.util.lang.StringUtils;
+import org.osgi.dto.DTO;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.ConfigurationPolicy;
 import org.osgi.service.component.annotations.Deactivate;
 import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.http.HttpService;
 import org.osgi.service.metatype.annotations.Designate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -127,25 +137,24 @@ public class HealthCheckExecutorServlet extends 
HttpServlet {
     private static final String CACHE_CONTROL_KEY = "Cache-control";
     private static final String CACHE_CONTROL_VALUE = "no-cache";
     private static final String CORS_ORIGIN_HEADER_NAME = 
"Access-Control-Allow-Origin";
-
-    private String[] servletPaths;
-
+    
     private String servletPath;
 
     private String corsAccessControlAllowOrigin;
 
     private Map<Result.Status, Integer> defaultStatusMapping;
     
+    // Key: Servlet Path | Value: Servlet Registration
+    private Map<String, ServiceRegistration<Servlet>> servletRegistrations;
+    
+    private BundleContext bundleContext;
     private long servletDefaultTimeout;
     private String[] servletDefaultTags;
     private String defaultFormat;
     private String[] allowedFormats;
     private boolean defaultCombineTagsWithOr;
     private boolean disableRequestConfiguration;
-
-    @Reference
-    private HttpService httpService;
-
+    
     @Reference
     HealthCheckExecutor healthCheckExecutor;
 
@@ -162,7 +171,10 @@ public class HealthCheckExecutorServlet extends 
HttpServlet {
     ResultTxtVerboseSerializer verboseTxtSerializer;
 
     @Activate
-    protected final void activate(final 
HealthCheckExecutorServletConfiguration configuration) {
+    protected final void activate(final 
HealthCheckExecutorServletConfiguration configuration, final BundleContext 
bundleContext) {
+       this.bundleContext = bundleContext;
+       this.servletRegistrations = new HashMap<>();
+       
         this.servletPath = configuration.servletPath();
         this.defaultStatusMapping = 
getStatusMapping(configuration.httpStatusMapping());
         this.servletDefaultTimeout = configuration.timeout();
@@ -183,7 +195,6 @@ public class HealthCheckExecutorServlet extends HttpServlet 
{
         this.disableRequestConfiguration = 
configuration.disable_request_configuration();
         
         if ( configuration.disabled() ) {
-            this.servletPaths = null;
             LOG.info("Health Check Servlet is disabled by configuration");
             return;
         }
@@ -193,7 +204,7 @@ public class HealthCheckExecutorServlet extends HttpServlet 
{
             servletPath, defaultStatusMapping, servletDefaultTimeout, 
             servletDefaultTags!=null ? Arrays.asList(servletDefaultTags): 
"<none>", defaultCombineTagsWithOr, defaultFormat, 
             Arrays.toString(this.allowedFormats), 
corsAccessControlAllowOrigin);
-        
+
         Map<String, HttpServlet> servletsToRegister = new 
LinkedHashMap<String, HttpServlet>();
         servletsToRegister.put(this.servletPath, this);
         if ( isFormatAllowed(FORMAT_HTML) ) {
@@ -214,30 +225,38 @@ public class HealthCheckExecutorServlet extends 
HttpServlet {
 
         for (final Map.Entry<String, HttpServlet> servlet : 
servletsToRegister.entrySet()) {
             try {
-                LOG.info("Registering HC servlet {} to path {}", 
getClass().getSimpleName(), servlet.getKey());
-                this.httpService.registerServlet(servlet.getKey(), 
servlet.getValue(), null, null);
+                LOG.info("Registering HC Servlet > Name: '{}', Path: '{}'", 
getClass().getSimpleName(),
+                           servlet.getKey());
+                ServletInfoDTO servletInfo = new 
ServletInfoDTO(configuration.servletContextName(),
+                        servlet.getKey(), servlet.getValue());
+                registerServlet(servletInfo);
             } catch (Exception e) {
                 LOG.error("Could not register health check servlet: " + e, e);
             }
         }
-        this.servletPaths = servletsToRegister.keySet().toArray(new String[0]);
     }
 
     @Deactivate
     public void deactivate() {
-        if (this.servletPaths == null) {
-            return;
-        }
-
-        for (final String servletPath : this.servletPaths) {
+        for (final Entry<String, ServiceRegistration<Servlet>> entry : 
servletRegistrations.entrySet()) {
             try {
-                LOG.info("Unregistering HC Servlet {} from path {}", 
getClass().getSimpleName(), servletPath);
-                this.httpService.unregister(servletPath);
+                LOG.info("Unregistering HC Servlet {} from path {}", 
getClass().getSimpleName(), entry.getKey());
+                entry.getValue().unregister();
             } catch (Exception e) {
-                LOG.error("Could not unregister health check servlet: " + e, 
e);
+                LOG.error("Could not unregister health check servlet", e);
             }
         }
-        this.servletPaths = null;
+        servletRegistrations.clear();
+    }
+
+    private void registerServlet(final ServletInfoDTO servletInfo) {
+       final Dictionary<String, Object> properties = new Hashtable<>();
+
+       properties.put(HTTP_WHITEBOARD_CONTEXT_SELECT, servletInfo.contextName);
+       properties.put(HTTP_WHITEBOARD_SERVLET_PATTERN, 
servletInfo.servletPath);
+       
+       final ServiceRegistration<Servlet> registration = 
bundleContext.registerService(Servlet.class, servletInfo.servlet, properties);
+       servletRegistrations.put(servletPath, registration);
     }
 
     /**
@@ -457,5 +476,17 @@ public class HealthCheckExecutorServlet extends 
HttpServlet {
             HealthCheckExecutorServlet.this.doGet(req, resp, null, format);
         }
     }
+    
+    private static class ServletInfoDTO extends DTO {
+       String contextName;
+       String servletPath;
+       Servlet servlet;
+       
+        public ServletInfoDTO(String contextName, String servletPath, Servlet 
servlet) {
+            this.contextName = contextName;
+            this.servletPath = servletPath;
+            this.servlet = servlet;
+        }
+    }
 
 }
diff --git 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletConfiguration.java
 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletConfiguration.java
index 7b7d4c95e6..adb48fab1a 100644
--- 
a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletConfiguration.java
+++ 
b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletConfiguration.java
@@ -24,11 +24,11 @@ import org.osgi.service.metatype.annotations.Option;
 @ObjectClassDefinition(name = "Apache Felix Health Check Executor Servlet", 
description = "Serializes health check results into html, json or txt format")
 @interface HealthCheckExecutorServletConfiguration {
 
-    String SERVLET_PATH_DEFAULT = "/system/health";
+       @AttributeDefinition(name = "Servlet Context Name", description = 
"Servlet Context Name")
+    String servletContextName() default "*";
 
-    @AttributeDefinition(name = "Path", description = "Servlet path (defaults 
to " + SERVLET_PATH_DEFAULT
-            + " in order to not be accessible via Apache/Internet)")
-    String servletPath() default SERVLET_PATH_DEFAULT;
+    @AttributeDefinition(name = "Servlet Path", description = "Servlet Path)")
+    String servletPath() default "/system/health";
 
     @AttributeDefinition(name = "Http Status Mapping", description = "Maps HC 
result status values to http response codes. Can be overwritten via request 
parameter 'httpStatus'")
     String httpStatusMapping() default 
"OK:200,WARN:200,CRITICAL:503,TEMPORARILY_UNAVAILABLE:503,HEALTH_CHECK_ERROR:500";
diff --git 
a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletTest.java
 
b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletTest.java
index a1fbf43c9c..87d9584403 100644
--- 
a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletTest.java
+++ 
b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletTest.java
@@ -51,6 +51,7 @@ import org.junit.Test;
 import org.mockito.ArgumentMatcher;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
 
@@ -82,6 +83,9 @@ public class HealthCheckExecutorServletTest {
 
     @Mock
     private ServiceReference hcServiceRef;
+    
+    @Mock
+    private BundleContext bundleContext;
 
     @Mock
     private PrintWriter writer;
@@ -106,7 +110,7 @@ public class HealthCheckExecutorServletTest {
             HealthCheckExecutorServlet.FORMAT_TXT,
             
HealthCheckExecutorServlet.FORMAT_VERBOSE_TXT}).when(healthCheckExecutorServletConfig).allowed_formats();
         doReturn("/hc").when(healthCheckExecutorServletConfig).servletPath();
-        healthCheckExecutorServlet.activate(healthCheckExecutorServletConfig);
+        healthCheckExecutorServlet.activate(healthCheckExecutorServletConfig, 
bundleContext);
     }
 
     @Test

Reply via email to