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