You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2023/08/25 05:52:17 UTC
[felix-dev] branch jakarta-servlet-6 updated: No need to branch healthchecks
This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch jakarta-servlet-6
in repository https://gitbox.apache.org/repos/asf/felix-dev.git
The following commit(s) were added to refs/heads/jakarta-servlet-6 by this push:
new 374fe22fe8 No need to branch healthchecks
374fe22fe8 is described below
commit 374fe22fe8f0e9ef0cb4edc79101674a1999e871
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Aug 25 07:52:08 2023 +0200
No need to branch healthchecks
---
healthcheck/README.md | 389 ---------
healthcheck/core/LICENSE | 201 -----
healthcheck/core/bnd.bnd | 20 -
healthcheck/core/pom.xml | 308 -------
.../felix/hc/core/impl/CompositeHealthCheck.java | 189 -----
.../impl/CompositeHealthCheckConfiguration.java | 43 -
.../apache/felix/hc/core/impl/CompositeResult.java | 47 -
.../core/impl/JmxAdjustableStatusHealthCheck.java | 232 -----
.../core/impl/commands/HealthCheckExecCommand.java | 88 --
.../core/impl/commands/HealthCheckListCommand.java | 98 ---
.../impl/executor/CombinedExecutionResult.java | 102 ---
.../hc/core/impl/executor/ExecutionResult.java | 124 ---
.../impl/executor/ExtendedHealthCheckExecutor.java | 41 -
.../impl/executor/HealthCheckExecutorImpl.java | 483 -----------
.../HealthCheckExecutorImplConfiguration.java | 51 --
.../executor/HealthCheckExecutorThreadPool.java | 111 ---
...HealthCheckExecutorThreadPoolConfiguration.java | 31 -
.../hc/core/impl/executor/HealthCheckFuture.java | 178 ----
.../core/impl/executor/HealthCheckResultCache.java | 225 -----
.../TempUnavailableGracePeriodEvaluator.java | 101 ---
.../executor/async/AsyncHealthCheckExecutor.java | 262 ------
.../AdhocResultDuringRequestProcessingFilter.java | 277 ------
.../core/impl/filter/ServiceUnavailableFilter.java | 461 ----------
.../hc/core/impl/monitor/HealthCheckMonitor.java | 373 --------
.../felix/hc/core/impl/monitor/HealthState.java | 261 ------
.../hc/core/impl/scheduling/AsyncIntervalJob.java | 64 --
.../felix/hc/core/impl/scheduling/AsyncJob.java | 33 -
.../hc/core/impl/scheduling/CronJobFactory.java | 80 --
.../cron/embedded/AsyncEmbeddedCronJob.java | 134 ---
.../cron/embedded/EmbeddedCronParser.java | 477 -----------
.../cron/embedded/EmbeddedCronScheduler.java | 75 --
.../embedded/EmbeddedCronSchedulerProvider.java | 87 --
.../scheduling/cron/quartz/AsyncQuartzCronJob.java | 124 ---
.../cron/quartz/QuartzCronScheduler.java | 117 ---
.../cron/quartz/QuartzCronSchedulerProvider.java | 55 --
.../impl/servlet/HealthCheckExecutorServlet.java | 501 -----------
.../HealthCheckExecutorServletConfiguration.java | 87 --
.../hc/core/impl/servlet/ResultHtmlSerializer.java | 196 -----
.../servlet/ResultHtmlSerializerConfiguration.java | 41 -
.../hc/core/impl/servlet/ResultJsonSerializer.java | 112 ---
.../hc/core/impl/servlet/ResultTxtSerializer.java | 30 -
.../impl/servlet/ResultTxtVerboseSerializer.java | 185 ----
.../ResultTxtVerboseSerializerConfiguration.java | 38 -
.../hc/core/impl/util/AdhocStatusHealthCheck.java | 55 --
.../felix/hc/core/impl/util/HealthCheckFilter.java | 146 ----
.../felix/hc/core/impl/util/lang/StringUtils.java | 52 --
.../apache/felix/hc/jmx/impl/HealthCheckMBean.java | 258 ------
.../felix/hc/jmx/impl/HealthCheckMBeanCreator.java | 205 -----
.../hc/core/impl/CompositeHealthCheckTest.java | 301 -------
.../impl/executor/HealthCheckExecutorImplTest.java | 180 ----
.../impl/executor/HealthCheckResultCacheTest.java | 194 -----
.../TempUnavailableGracePeriodEvaluatorTest.java | 108 ---
.../impl/filter/ServiceUnavailableFilterTest.java | 100 ---
.../core/impl/monitor/HealthCheckMonitorTest.java | 475 -----------
.../cron/embedded/EmbeddedCronParserTest.java | 944 ---------------------
.../cron/embedded/EmbeddedCronSchedulerTest.java | 147 ----
.../servlet/HealthCheckExecutorServletTest.java | 323 -------
.../impl/servlet/ResultJsonSerializerTest.java | 72 --
.../servlet/ResultTxtVerboseSerializerTest.java | 39 -
.../hc/core/impl/util/HealthCheckFilterTest.java | 109 ---
.../felix/hc/core/impl/util/StringUtilsTest.java | 46 -
.../felix/hc/core/it/AsyncHealthCheckIT.java | 216 -----
.../hc/core/it/ExtendedHealthCheckExecutorIT.java | 111 ---
.../hc/core/it/HealthCheckExecutorSelectionIT.java | 147 ----
.../felix/hc/core/it/HealthCheckFilterIT.java | 283 ------
.../felix/hc/core/it/HealthCheckServletIT.java | 215 -----
.../core/it/JmxAdjustableStatusHealthCheckIT.java | 157 ----
.../test/java/org/apache/felix/hc/core/it/U.java | 180 ----
.../felix/hc/jmx/impl/HealthCheckMBeanTest.java | 121 ---
healthcheck/webconsoleplugin/LICENSE | 201 -----
healthcheck/webconsoleplugin/bnd.bnd | 5 -
healthcheck/webconsoleplugin/pom.xml | 146 ----
.../impl/HealthCheckWebconsolePlugin.java | 414 ---------
.../felix/hc/webconsole/impl/WebConsoleHelper.java | 92 --
.../src/main/resources/res/ui/healthcheck.css | 51 --
.../impl/HealthCheckWebconsolePluginTest.java | 95 ---
76 files changed, 13320 deletions(-)
diff --git a/healthcheck/README.md b/healthcheck/README.md
deleted file mode 100644
index 2f9a76a3c0..0000000000
--- a/healthcheck/README.md
+++ /dev/null
@@ -1,389 +0,0 @@
-
-# Felix Health Checks
-
-Based on a simple `HealthCheck` SPI interface, Felix Health Checks are used to check the health/availability of Apache Felix instances at runtime based on inputs like
-
-* OSGi framework status
-* JMX MBean attribute values
-* OSGi service(s) / SCR component(s) availablility
-* ... or any context information retrievable via any API
-
-Health checks are easily extensible either by
-
-* configuring the default HealthCheck services in bundle general checks (can be configuration or scripts, see [out-of-the-box checks](#general-purpose-health-checks-available-out-of-the-box)). **For simple setups, the out-of-the-box health checks are often sufficient**
-* or by implementing your own HealthCheck to cater special requirements (this is done by just registering a service for this interface)
-
-There are various ways to [execute health checks](#executing-health-checks) - this is a good starting point to get familiar with how health checks work.
-
-See also:
-
-* [Source code for the HealthCheck modules](http://svn.apache.org/repos/asf/felix/trunk/healthcheck)
-* adaptTo() slides about Health Checks (from the time when they were part of Apache Sling):
- * [adaptTo() 2019 - Felix Health Checks](https://adapt.to/2019/en/schedule/felix-health-checks.html)
- * [adaptTo() 2014 - New features of the Sling Health Check](https://adapt.to/2014/en/schedule/new-features-of-the-sling-health-check.html)
- * [adaptTo() 2013 - Automated self-testing and health check of live Sling instances](https://adapt.to/2013/en/schedule/18_healthcheck.html)
-
-## Use cases
-Generally health checks have two high level use cases:
-
-* **Load balancers/orchestration engines can query the health of an instance** and decide when to route requests to it
-* **Operations teams checking instances** for their internal state **manually**
-
-The strength of Health Checks are to surface internal state for external use:
-
-* Check that all OSGi bundles are up and running
-* Verify that performance counters are in range
-* Ping external systems and raise alarms if they are down
-* Run smoke tests at system startup
-* Check that demo content has been removed from a production system
-* Check that demo accounts are disabled
-
-The health check subsystem uses tags to select which health checks to execute so you can for example execute just the _performance_ or _security_ health
-checks once they are configured with the corresponding tags.
-
-The out of the box health check services also allow for using them as JMX aggregators and processors, which take JMX
-attribute values as input and make the results accessible via JMX MBeans.
-
-## Implementing `HealthCheck`s
-
-Health checks can be contributed by any bundle via the provided SPI interface. It is best practice to implement a health check as part of the bundle that contains the functionality being checked.
-
-## The `HealthCheck` SPI interface
-
-A `HealthCheck` is just an OSGi service that returns a `Result`.
-
-```
- public interface HealthCheck {
-
- /** Execute this health check and return a {@link Result}
- * This is meant to execute quickly, access to external
- * systems, for example, should be managed asynchronously.
- */
- public Result execute();
- }
-```
-
-A simple health check implementation might look like follows:
-
-```
- public class SampleHealthCheck implements HealthCheck {
-
- @Override
- public Result execute() {
- FormattingResultLog log = new FormattingResultLog();
- ...
- log.info("Checking my context {}", myContextObject);
- if(myContextObject.retrieveStatus() != ...expected value...) {
- log.warn("Problem with ...");
- }
- if(myContextObject.retrieveOtherStatus() != ...expected value...) {
- log.critical("Cricital Problem with ...");
- }
- return new Result(log);
- }
-
- }
-```
-
-The `Result` is a simple immutable class that provides a `Status` via `getStatus()` (OK, WARN, CRITICAL etc.) and one or more log-like messages that
-can provide more info about what, if anything, went wrong.
-
-Instead of using Log4j side by side with ResultLog/FormattingResultLog it is recommended to turn on `autoLogging` in the [health check executor config](#configuring-the-health-check-executor) in order to keep the implementation classes DRY. **NOTE:** This feature does not work with checks implemented against the legacy Sling interface.
-
-### Semantic meaning of health check results
-In order to make health check results aggregatable in a reasonable way, it is important that result status values are used in a consistent way across different checks. When implementing custom health checks, comply to the following table:
-
-Status | System is functional | Meaning | Actions possible for machine clients | Actions possible for human clients
---- | --- | --- | --- | ---
-OK | yes | Everything is ok. | <ul><li>If system is not actively used yet, a load balancer might decide to take the system to production after receiving this status for the first time.</li><li>Otherwise no action needed</li></ul> | Response logs might still provide information to a human on why the system currently is healthy. E.g. it might show 30% disk used which indicates that no action will be required for a long time
-WARN | yes | **Tendency to CRITICAL** <br>System is fully functional but actions are needed to avoid a CRITICAL status in the future | <ul><li>Certain actions can be configured for known, actionable warnings, e.g. if disk space is low, it could be dynamically extended using infrastructure APIs if on virtual infrastructure)</li><li>Pass on information to monitoring system to be available to humans (in other aggregator UIs)</li></ul> | Any manual steps that a human can perform based on the [...]
-TEMPORARILY_UNAVAILABLE *) | no | **Tendency to OK** <br>System is not functional at the moment but is expected to become OK (or at least WARN) without action. An health check using this status is expected to turn CRITICAL after a certain period returning TEMPORARILY_UNAVAILABLE | <ul><li>Take out system from load balancing</li><li>Wait until TEMPORARILY_UNAVAILABLE status turns into either OK or CRITICAL</li></ul> | Wait and monitor result logs of health check returning TEMPORARILY_UNAVAILABLE
-CRITICAL | no | System is not functional and must not be used | <ul><li>Take out system from load balancing</li><li>Decommission system entirely and re-provision from scratch</li></ul> | Any manual steps that a human can perform based on their knowledge to bring the system back to state OK
-HEALTH\_CHECK\_ERROR | no | **Actual status unknown** <br>There was an error in correctly calculating one of the status values above. Like CRITICAL but with the hint that the health check probe itself might be the problem (and the system could well be in state OK) | <ul><li>Treat exactly the same as CRITICAL</li></ul> | Fix health check implementation or configuration to ensure a correct status can be calculated
-
-*) The health check executor automatically turns checks that coninuosly return `TEMPORARILY_UNAVAILABLE` into `CRITICAL` after a certain grace period, see [Configuring the Health Check Executor](#configuring-the-health-check-executor)
-
-### Configuring Health Checks
-
-`HealthCheck` services are created via OSGi configurations. Generic health check service properties are interpreted by the health check executor service. Custom health check service properties can be used by the health check implementation itself to configure its behaviour.
-
-The following generic Health Check properties may be used for all checks (**all service properties are optional**):
-
-Property | Type | Description
------------ | -------- | ------------
-hc.name | String | The name of the health check as shown in UI
-hc.tags | String[] | List of tags: Both Felix Console Plugin and Health Check servlet support selecting relevant checks by providing a list of tags
-hc.mbean.name | String | Makes the HC result available via given MBean name. If not provided no MBean is created for that `HealthCheck`
-hc.async.cronExpression | String | Executes the health check asynchronously using the cron expression provided. Use this for **long running health checks** to avoid execution every time the tag/name is queried. Prefer configuring a HealthCheckMonitor if you only want to regularly execute a HC.
-hc.async.intervalInSec | Long | Async execution like `hc.async.cronExpression` but using an interval
-hc.resultCacheTtlInMs | Long | Overrides the global default TTL as configured in health check executor for health check responses
-hc.keepNonOkResultsStickyForSec | Long | If given, non-ok results from past executions will be taken into account as well for the given seconds (use Long.MAX_VALUE for indefinitely). Useful for unhealthy system states that disappear but might leave the system at an inconsistent state (e.g. an event queue overflow where somebody needs to intervene manually) or for checks that should only go back to OK with a delay (can be useful for load balancers).
-
-### Annotations to simplify configuration of custom Health Checks
-
-To configure the defaults for the service properties [above](#configuring-health-checks), the following annotations can be used:
-
- // standard OSGi
- @Component
- @Designate(ocd = MyCustomCheckConfig.class, factory = true)
-
- // to set `hc.name` and `hc.tags`
- @HealthCheckService(name = "Custom Check Name", tags= {"tag1", "tag2"})
-
- // to set `hc.async.cronExpression` or `hc.async.intervalInSec`
- @Async(cronExpression="0 0 12 1 * ?" /*, intervalInSec = 60 */)
-
- // to set `hc.resultCacheTtlInMs`:
- @ResultTTL(resultCacheTtlInMs = 10000)
-
- // to set `hc.mbean.name`:
- @HealthCheckMBean(name = "MyCustomCheck")
-
- // to set `hc.keepNonOkResultsStickyForSec`:
- @Sticky(keepNonOkResultsStickyForSec = 10)
- public class MyCustomHealthCheck implements HealthCheck {
- ...
-
-
-## General purpose health checks available out-of-the-box
-
-The following checks are contained in bundle `org.apache.felix.healthcheck.generalchecks` and can be activated by simple configuration:
-
-Check | PID | Factory | Description
---- | --- | --- | ---
-Framework Startlevel | org.apache.felix.hc.generalchecks.FrameworkStartCheck | no | Checks the OSGi framework startlevel - `targetStartLevel` allows to configure a target start level, `targetStartLevel.propName` can be used to read it from the framework/system properties.
-Services Ready | org.apache.felix.hc.generalchecks.ServicesCheck | yes | Checks for the existance of the given services. `services.list` can contain simple service names or filter expressions
-Components Ready | org.apache.felix.hc.generalchecks.DsComponentsCheck | yes | Checks for the existance of the given components. Use `components.list` to list required active components (use component names)
-Bundles Started | org.apache.felix.hc.generalchecks.BundlesStartedCheck | yes | Checks for started bundles - `includesRegex` and `excludesRegex` control what bundles are checked.
-Disk Space | org.apache.felix.hc.generalchecks.DiskSpaceCheck | yes | Checks for disk space usage at the given paths `diskPaths` and checks them against thresholds `diskUsedThresholdWarn` (default 90%) and diskUsedThresholdCritical (default 97%)
-Memory | org.apache.felix.hc.generalchecks.MemoryCheck | no | Checks for Memory usage - `heapUsedPercentageThresholdWarn` (default 90%) and `heapUsedPercentageThresholdCritical` (default 99%) can be set to control what memory usage produces status `WARN` and `CRITICAL`
-CPU | org.apache.felix.hc.generalchecks.CpuCheck | no | Checks for CPU usage - `cpuPercentageThresholdWarn` (default 95%) can be set to control what CPU usage produces status `WARN` (check never results in `CRITICAL`)
-Thread Usage | org.apache.felix.hc.generalchecks.ThreadUsageCheck | no | Checks via `ThreadMXBean.findDeadlockedThreads()` for deadlocks and analyses the CPU usage of each thread via a configurable time period (`samplePeriodInMs` defaults to 200ms). Uses `cpuPercentageThresholdWarn` (default 95%) to `WARN` about high thread utilisation.
-JMX Attribute Check | org.apache.felix.hc.generalchecks.JmxAttributeCheck | yes | Allows to check an arbitrary JMX attribute (using the configured mbean `mbean.name`'s attribute `attribute.name`) against a given constraint `attribute.value.constraint` (see [Constraints](#constraints)). Can check multiple attributes by providing additional config properties with numbers: `mbean2.name` (defaults to `mbean.name` if ommitted), `attribute2.name` and `attribute2.value.constraint` and `mbean3.n [...]
-Http Requests Check | org.apache.felix.hc.generalchecks.HttpRequestsCheck | yes | Allows to check a list of URLs against response code, response headers, timing, response content (plain content via RegEx or JSON via path expression). See [Request Spec Syntax](#request-spec-syntax)
-Scripted Check | org.apache.felix.hc.generalchecks.ScriptedHealthCheck | yes | Allows to run an arbitrary script. To configure use either `script` to provide a script directly or `scriptUrl` to link to an external script (may be a file URL or a link to a JCR file if a Sling Repository exists, e.g. `jcr:/etc/hc/check1.groovy`). Use the `language` property to refer to a registered script engine (e.g. install bundle `groovy-all` to be able to use language `groovy`). The script has the bindi [...]
-
-### Constraints
-
-The `JMX Attribute Check` and `Http Requests Check` allow to check values against contraints. See the following examples:
-
-* value `string value` (checks for equality)
-* value ` = 0`
-* value ` > 0`
-* value ` < 100`
-* value ` BETWEEN 3 AND 7`
-* value ` CONTAINS a string to find in value` (searches for `a string to find in value` in value)
-* value ` STARTS_WITH prefix string` (checks for prefix `prefix string`)
-* value ` ENDS_WITH suffixStr` (checks for suffix `suffixStr `)
-* value ` MATCHES ^.*SomeRegEx[0-9]$` (checks for the given RegEx)
-* value ` OLDER_THAN 100 ms` (checks a time value to be older than given value, other units `s`, `min`, `h`, `days` are supported as well, e.g. ` OLDER_THAN 10 days`)
-* `NOT` prefix works for all expressions, e.g. `NOT 20`, `NOT > 20`, `NOT BETWEEN 3 AND 7`, `NOT MATCHES ^.*SomeRegEx[0-9]$`
-
-Also see class `org.apache.felix.hc.generalchecks.util.SimpleConstraintsChecker` and its JUnit test.
-
-### Request Spec Syntax
-
-The `Http Requests Check` allows to configure a list of request specs. Requests specs have two parts: Before `=>` can be a simple URL/path with curl-syntax advanced options (e.g. setting a header with `-H "Test: Test val"`), after the `=>` it is a simple response code that can be followed ` && MATCHES <RegEx>` to match the response entity against or other matchers like HEADER, TIME or JSON.
-
-Examples:
-
-* `/path/example.html`: assumes 200 for the request to localhost:<defaultPort>
-* `http://www.example.com/path/example.html => 200`: explicitly checking 200 response code for full URL
-* `/protected/example.html => 401`: protected page
-* `-u admin:admin /protected/example.html => 200`: protected page with password (only use for non-sensitive credentials)
-* `/path/example.html => 200 && MATCHES <title>html title.*</title>`: ensure 200 response and matching content
-* `/path/example.html => 200 && HEADER Content-Type MATCHES text/html.*`: Checks for content type
-* `/path/example.json => 200 && JSON [3].prop = myval`: checks JSON response's third element for property `prop` to equal to `myval`
-* `-H "Test: Test val" /path/example.json => 200 && JSON .city STARTS_WITH New`: checks JSON response's `city` property to start with `New`
-* `/path/example-timing-important.html => 200 && TIME < 5 ms`: Checks if the response time is smaller than specified
-
-All constraints from [Constraints](#constraints) can be used.
-
-### Adjustable Status Health Check
-
-This is a health check that can be dynamically controlled via JMX bean `org.apache.felix.healthcheck:type=AdjustableStatusHealthCheck`. It allows to dynamically add a health check that returns `WARN` (operation `addWarnResultForTags(String)`), `CRITICAL` (operation `addCriticalResultForTags(String)`) or `TEMPORARILY_UNAVAILABLE` (operation `addTemporarilyUnavailableResultForTags(String)`) for certain tags. This is useful for testing purposes or go-live sequences. The operation `reset()` [...]
-
-## Executing Health Checks
-
-Health Checks can be executed via a [webconsole plugin](#webconsole-plugin), the [health check servlet](#health-check-servlet) or via [JMX](#jmx-access-to-health-checks). `HealthCheck` services can be selected for execution based on their `hc.tags` multi-value service property.
-
-The `HealthCheckFilter` utility accepts positive and negative tag parameters, so that `osgi,-security`
-selects all `HealthCheck` having the `osgi` tag but not the `security` tag, for example.
-
-For advanced use cases it is also possible to use the API directly by using the interface `org.apache.felix.hc.api.execution.HealthCheckExecutor`.
-
-### Configuring the Health Check Executor
-The health check executor can **optionally** be configured via service PID `org.apache.felix.hc.core.impl.executor.HealthCheckExecutorImpl`:
-
-Property | Type | Default | Description
------------ | -------- | ------ | ------------
-`timeoutInMs` | Long | 2000ms | Timeout in ms until a check is marked as timed out
-`longRunningFutureThresholdForCriticalMs` | Long | 300000ms (5min) | Threshold in ms until a check is marked as 'exceedingly' timed out and will marked CRITICAL instead of WARN only
-`resultCacheTtlInMs` | Long | 2000ms | Result Cache time to live - results will be cached for the given time
-`temporarilyAvailableGracePeriodInMs` | Long | 60000ms (10min) | After this configured period, health checks continously reporting `TEMPORARILY_UNAVAILABLE` are automatically turned into status `CRITICAL`
-`autoLogging` | Boolean | false | If enabled, will automatically log entries of ResultLog (or FormattingResultLog resp.) using Log4j. The logging category used is the class instantiating ResultLog prefixed with 'healthchecks.', for instance 'healthchecks.com.mycorp.myplatform.mymodule.ModuleCheck'. The prefix allows for easy configuration of a log file containing all health check results.
-
-### JMX access to health checks
-
-Health checks that define the service property `hc.mbean.name` will automatically get the JMX bean with that name, the domain `org.apache.felix.healthcheck` and with the type `HealthCheck` registered. The bean provides access to the `Result` (status, logs, etc.)
-
-### Health Check Servlet
-The health check servlet allows to query the checks via http. It provides
-similar features to the Web Console plugin described above, with output in HTML, JSON (plain or jsonp) and TXT (concise or verbose) formats (see HTML format rendering page for more documentation).
-
-The Health Checks Servlet is disabled by default, to enable it create an OSGi configuration like
-
- PID = org.apache.felix.hc.core.impl.servlet.HealthCheckExecutorServlet
- servletPath = /system/health
-
-which specifies the servlet's base path. That URL then returns an HTML page, by default with the results of all active health checks and
-with instructions at the end of the page about URL parameters which can be used to select specific Health Checks and control their execution and output format.
-
-Note that by design **the Health Checks Servlet doesn't do any access control by itself** to ensure it can detect unhealthy states of the authentication itself. Make sure the configured path is only accessible to relevant infrastructure and operations people. Usually all `/system/*` paths are only accessible from a local network and not routed to the Internet.
-
-By default the HC servlet sends the CORS header `Access-Control-Allow-Origin: *` to allow for client-side browser integrations. The behaviour can be configured using the OSGi config property `cors.accessControlAllowOrigin` (a blank value disables the header).
-
-### Webconsole plugin
-
-If the `org.apache.felix.hc.webconsole` bundle is installed, a webconsole plugin
-at `/system/console/healthcheck` allows for executing health checks, optionally selected
-based on their tags (positive and negative selection, see the `HealthCheckFilter` mention above).
-
-The DEBUG logs of health checks can optionally be displayed, and an option allows for showing only health checks that have a non-OK status.
-
-### Gogo Console
-
-The Gogo command `hc:exec` can be used as follows:
-
- hc:exec [-v] [-a] tag1,tag2
- -v verbose/debug
- -a combine tags with and logic (instead of or logic)
-
-The command is available without installing additional bundles (it is included in the core bundle `org.apache.felix.healthcheck.core`)
-
-## Monitoring Health Checks
-
-### Setting up a monitor configuration
-
-By default, health checks are only executed if explicitly triggered via one of the mechanisms as described in [Executing Health Checks](#executing-health-checks) (servlet, web console plugin, JMX, executor API). With the `HealthCheckMonitor`, Health checks can be regularly monitored by configuring the the **factory PID** `org.apache.felix.hc.core.impl.monitor.HealthCheckMonitor` with the following properties:
-
-Property | Type | Default | Description
------------ | -------- | ------ | ------------
-`tags` and/or `names` | String[] | none, at least one of the two is required | **Will regularly call all given tags and/or names**. All given tags/names are executed in parallel. If the set of tags/names include some checks multiple times it does not matter, the `HealthCheckExecutor` will always ensure checks are executed once at a time only.
-`intervalInSec` or `cronExpression` | Long or String (cron) | none, one of the two is required | The interval in which the given tags/names will be executed
-`registerHealthyMarkerService` | boolean | true | For the case a given tag/name is healthy, will register a service `org.apache.felix.hc.api.condition.Healthy` with property tag=<tagname> (or name=<hc.name>) that other services can depend on. For the special case of the tag `systemready`, the marker service `org.apache.felix.hc.api.condition.SystemReady` is registered
-`registerUnhealthyMarkerService` | boolean | false | For the case a given tag/name is **un**healthy, will register a service `org.apache.felix.hc.api.condition.Unhealthy` with property tag=<tagname> (or name=<hc.name>) that other services can depend on
-`treatWarnAsHealthy` | boolean | true | `WARN` usually means [the system is usable](#semantic-meaning-of-health-check-results), hence WARN is treated as healthy by default. When set to false `WARN` is treated as `Unhealthy`
-`sendEvents` | enum `NONE`, `STATUS_CHANGES`, `STATUS_CHANGES_OR_NOT_OK` or `ALL` | `STATUS_CHANGES` | Whether to send events for health check status changes. See [below](#osgi-events-for-health-check-status-changes) for details.
-`logResults` | enum `NONE`, `STATUS_CHANGES`, `STATUS_CHANGES_OR_NOT_OK` or `ALL` | `NONE ` | Whether to log the result of the monitor to the regular log file
-`logAllResultsAsInfo` | boolean | false | If `logResults` is enabled and this is enabled, all results will be logged with INFO log level. Otherwise WARN and INFO are used depending on the health state.
-`isDynamic` | boolean | false | In dynamic mode all checks for names/tags are monitored individually (this means events are sent/services registered for name only, never for given tags). This mode allows to use `*` in tags to query for all health checks in system. It is also possible to query for all except certain tags by using `-`, e.g. by configuring the values `*`, `-tag1` and `-tag2` for `tags`.
-
-### Marker Service to depend on a health status in SCR Components
-
-It is possible to use OSGi service references to depend on the health status of a certain
-tag or name. For that to work, a `HealthCheckMonitor` needs to be configured for the relevant tag or name. To depend on a health status in a component, use a `@Reference` to one of the marker services `Healthy`, `Unhealthy` and `SystemReady` - this will then automatically activate/deactivate the component based on the certain health status. To activate a component only upon healthiness of a certain tag/name use the following code:
-
-```
- @Reference(target="(tag=dbavail)")
- Healthy healthy;
-
- @Reference(target="(name=My Health Check)")
- Healthy healthy;
-```
-For the special tag `systemready`, there is a convenience marker interface available:
-
-```
- @Reference
- SystemReady systemReady;
-```
-It is also possible to depend on a unhealthy state (e.g. for fallback functionality or self-healing):
-
-```
- @Reference(target="(tag=dbavail)")
- Unhealthy unhealthy;
-```
-
-NOTE: This does not support the [RFC 242 Condition Service](https://github.com/osgi/design/blob/master/rfcs/rfc0242/rfc-0242-Condition-Service.pdf) yet - however once final the marker services will also be able to implement the `Condition` interface.
-
-### OSGi events for Health Check status changes and updates
-
-OSGi events with topic `org/apache/felix/health/*` are sent for tags/names that a `HealthCheckMonitor` is configured for and if `sendEvents` is set to `STATUS_CHANGES` or `ALL`:
-
-* `STATUS_CHANGES` notifies only of status changes with suffix `/STATUS_CHANGED`
-* `ALL` sends events whenever the monitor runs, depending on status will either send the event with suffix `/UPDATED` or `/STATUS_CHANGED`
-
-All events sent generally carry the properties `executionResult`, `status` and `previousStatus`.
-
-| Example | Description
-------- | -----
-`org/apache/felix/health/tag/mytag/STATUS_CHANGED` | Status for tag `mytag` has changed compared to last execution
-`org/apache/felix/health/tag/My_HC_Name/UPDATED ` (spaces in names are replaced with underscores to ensure valid topic names) | Status for name `My HC Name` has not changed but HC was executed and execution result is available in event property `executionResult`.
-`org/apache/felix/health/component/com/myprj/MyHealthCheck/UPDATED` (`.` are replaced with slashes to produce valid topic names) | HC based on SCR component `com.myprj.MyHealthCheck` was executed without having the status changed. The SCR component event is sent in addition to the name event
-
-Event listener example:
-
-```
-@Component(property = { EventConstants.EVENT_TOPIC + "=org/apache/felix/health/*"})
-public class HealthEventHandler implements EventHandler {
- private static final Logger LOG = LoggerFactory.getLogger(HealthEventHandler.class);
-
- public void handleEvent(Event event) {
- LOG.info("Received event: "+event.getTopic());
- LOG.info(" previousStatus: "+event.getProperty("previousStatus"));
- LOG.info(" status: "+event.getProperty("status"));
- HealthCheckExecutionResult executionResult = (HealthCheckExecutionResult) event.getProperty("executionResult");
- LOG.info(" executionResult: "+executionResult);
- LOG.info(" result: "+executionResult.getHealthCheckResult());
- }
-}
-```
-
-## Servlet Filters
-
-### Service Unavailable Filter
-
-For health states of the system that mean that requests can only fail it is possible to configure a Service Unavailable Filter that will cut off all requests if certain tags are in a `CRITICAL` or `TEMPORARILY_UNAVAILABLE` status. Typical usecases are startup/shutdown and deployments. Other scenarios include maintenance processes that require request processing of certain servlets to be stalled (the filter can be configured to be active on arbitrary paths). It is possible to configure a [...]
-
-Configure the factory configuration with PID
-`org.apache.felix.hc.core.impl.filter.ServiceUnavailableFilter` with specific parameters to activate the Service Unavailable Filter:
-
-| Name | Default/Required | Description |
-| --- | --- | --- |
-| `osgi.http.whiteboard.filter.regex` | required | Regex path on where the filter is active, e.g. `(?!/system/).*` or `.*`. See Http Whiteboard documentation [1] and hint [2] |
-| `osgi.http.whiteboard.context.select` | required | OSGi service filter for selecting relevant contexts, e.g. `(osgi.http.whiteboard.context.name=*)` selects all contexts. See Http Whiteboard documentation [1] and hint [2] |
-| `tags` | required | List of tags to query the status in order to decide if it is 503 or not |
-| `statusFor503 ` | default `TEMPORARILY_UNAVAILABLE` | First status that causes a 503 response. The default `TEMPORARILY_UNAVAILABLE` will not send 503 for `OK` and `WARN` but for `TEMPORARILY_UNAVAILABLE`, `CRITICAL` and `HEALTH_CHECK_ERROR` |
-| `includeExecutionResult ` | `false` | Will include the execution result in the response (as html comment for html case, otherwise as text). |
-| `responseTextFor503 ` | required | Response text for 503 responses. Value can be either the content directly (e.g. just the string `Service Unavailable`) or in the format `classpath:<symbolic-bundle-id>:/path/to/file.html` (it uses `Bundle.getEntry()` to retrieve the file). The response content type is auto-detected to either `text/html` or `text/plain`. |
-| `autoDisableFilter ` | default `false` | If true, will automatically disable the filter once the filter continued the filter chain without 503 for the first time. The filter will be automatically enabled again if the start level of the framework changes (hence on shutdown it will be active again). Useful for server startup scenarios.|
-| `avoid404DuringStartup` | default `false` | If true, will automatically register a dummy servlet to ensure this filter becomes effective (complex applications might have the http whiteboard active but no servlets be active during early phases of startup, a filter only ever becomes active if there is a servlet registered). Useful for server startup scenarios. |
-| `service.ranking` | default `Integer.MAX_VALUE` (first in filter chain) | The `service.ranking` for the filter as respected by http whiteboard [1]. |
-
-[1] [https://felix.apache.org/documentation/subprojects/apache-felix-http-service.html#filter-service-properties](https://felix.apache.org/documentation/subprojects/apache-felix-http-service.html#filter-service-properties)
-
-[2] Choose a combination of `osgi.http.whiteboard.filter.regex`/ `osgi.http.whiteboard.context.select` wisely, e.g. `osgi.http.whiteboard.context.select=(osgi.http.whiteboard.context.name=*)` and `osgi.http.whiteboard.filter.regex=.*` would also cut off all admin paths.
-
-### Adding ad hoc results during request processing
-
-For certain scenarios it is useful to add a health check dynamically for a specific tag durign request processing, e.g. it can be useful during deployment requests (the tag(s) being added can be queried by e.g. load balancer or Service Unavailable Filter.
-
-To achieve this configure the factory configuration with PID
-`org.apache.felix.hc.core.impl.filter.AdhocResultDuringRequestProcessingFilter` with specific parameters:
-
-| Name | Default/Required | Description |
-| --- | --- | --- |
-| `osgi.http.whiteboard.filter.regex` | required | Regex path on where the filter is active, e.g. `(?!/system/).*` or `.*`. See Http Whiteboard documentation |
-| `osgi.http.whiteboard.context.select` | required | OSGi service filter for selecting relevant contexts, e.g. `(osgi.http.whiteboard.context.name=*)` selects all contexts. See Http Whiteboard |
-| `service.ranking` | default `0` | The `service.ranking` for the filter as respected by http whiteboard [1]. |
-| `method` | default restriction not active | Relevant request method (leave empty to not restrict to a method) |
-| `userAgentRegEx` | default restriction not active | Relevant user agent header (leave emtpy to not restrict to a user agent) |
-| `hcName` | required | Name of health check during request processing |
-| `tags` | required | List of tags the adhoc result shall be registered for (tags are not active during configured delay in case 'delayProcessingInSec' is configured) |
-| `statusDuringRequestProcessing` | default `TEMPORARILY_UNAVAILABLE` | Status to be sent during request processing |
-| `delayProcessingInSec` | default `0` (not active) | Time to delay processing of request in sec (the default 0 turns the delay off). Use together with 'tagsDuringDelayedProcessing' advertise request processing before actual action (e.g. to signal a deployment request to a periodically querying load balancer before deployment starts) |
-| `tagsDuringDelayedProcessing` | default `0` (not active) | List of tags the adhoc result is be registered also during waiting for the configured delay |
-| `waitAfterProcessing.forTags` | default empty (not active) | List of tags to be waited for after processing (leave empty to not wait). While waiting the tags from property `tags` remain in configured state. |
-| `waitAfterProcessing.initialWait` | 3 sec | Initial waiting time in sec until 'waitAfterProcessing.forTags' are checked for the first time. |
-| `waitAfterProcessing.maxDelay` | 120 sec | Maximum delay in sec that can be caused when 'waitAfterProcessing.forTags' is configured (waiting is aborted after that time) |
-
diff --git a/healthcheck/core/LICENSE b/healthcheck/core/LICENSE
deleted file mode 100644
index 261eeb9e9f..0000000000
--- a/healthcheck/core/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/healthcheck/core/bnd.bnd b/healthcheck/core/bnd.bnd
deleted file mode 100644
index 1f67407eca..0000000000
--- a/healthcheck/core/bnd.bnd
+++ /dev/null
@@ -1,20 +0,0 @@
-Bundle-Category: healthcheck
-
-Bundle-Description: ${project.description}
-
-Bundle-DocURL: https://felix.apache.org
-
-Bundle-License: Apache License, Version 2.0
-
-Bundle-Vendor: The Apache Software Foundation
-
-Import-Package:\
- org.quartz*;resolution:=optional,\
- jakarta.servlet*;resolution:=optional,\
- *
-
-Conditional-Package: org.apache.felix.utils.*
-
--removeheaders:\
- Include-Resource,\
- Private-Package
diff --git a/healthcheck/core/pom.xml b/healthcheck/core/pom.xml
deleted file mode 100644
index 8bcf6abff3..0000000000
--- a/healthcheck/core/pom.xml
+++ /dev/null
@@ -1,308 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.apache.felix</groupId>
- <artifactId>felix-parent</artifactId>
- <version>8-SNAPSHOT</version>
- <relativePath />
- </parent>
-
- <artifactId>org.apache.felix.healthcheck.core</artifactId>
- <version>4.0.0-SNAPSHOT</version>
-
- <name>Apache Felix Health Check Core</name>
- <inceptionYear>2013</inceptionYear>
-
- <description>
- The Felix Health Check Core
- </description>
-
- <properties>
- <felix.java.version>17</felix.java.version>
- <pax-exam.version>4.13.4</pax-exam.version>
- <pax-link.version>2.6.7</pax-link.version>
- <org.ops4j.pax.logging.DefaultServiceLog.level>INFO</org.ops4j.pax.logging.DefaultServiceLog.level>
- <felix.shell>false</felix.shell>
- <project.bundle.file>${project.build.directory}/${project.build.finalName}.jar</project.bundle.file>
- <project.build.outputTimestamp>1644843466</project.build.outputTimestamp>
- </properties>
-
- <scm>
- <connection>scm:git:https://github.com/apache/felix-dev.git</connection>
- <developerConnection>scm:git:https://github.com/apache/felix-dev.git</developerConnection>
- <url>https://gitbox.apache.org/repos/asf?p=felix-dev.git</url>
- <tag>HEAD</tag>
- </scm>
-
- <build>
- <plugins>
- <plugin>
- <groupId>biz.aQute.bnd</groupId>
- <artifactId>bnd-maven-plugin</artifactId>
- <version>5.3.0</version>
- <executions>
- <execution>
- <goals>
- <goal>bnd-process</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- <configuration>
- <archive>
- <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
- </archive>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.jacoco</groupId>
- <artifactId>jacoco-maven-plugin</artifactId>
- <version>0.8.10</version>
- <executions>
- <execution>
- <id>prepare-agent-integration</id>
- <goals>
- <goal>prepare-agent-integration</goal>
- </goals>
- <configuration>
- <propertyName>coverage.command</propertyName>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.servicemix.tooling</groupId>
- <artifactId>depends-maven-plugin</artifactId>
- <version>1.4.0</version>
- <executions>
- <execution>
- <goals>
- <goal>generate-depends-file</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-failsafe-plugin</artifactId>
- <version>3.0.0-M5</version>
- <executions>
- <execution>
- <goals>
- <goal>integration-test</goal>
- <goal>verify</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <systemProperties>
- <org.ops4j.pax.logging.DefaultServiceLog.level>${org.ops4j.pax.logging.DefaultServiceLog.level}</org.ops4j.pax.logging.DefaultServiceLog.level>
- <felix.shell>${felix.shell}</felix.shell>
- <coverage.command>${coverage.command}</coverage.command>
- <project.bundle.file>${project.bundle.file}</project.bundle.file>
- </systemProperties>
- <useModulePath>false</useModulePath>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
- <dependencies>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>osgi.core</artifactId>
- <version>8.0.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.cm</artifactId>
- <version>1.6.1</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.component</artifactId>
- <version>1.5.1</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.component.annotations</artifactId>
- <version>1.5.1</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.event</artifactId>
- <version>1.4.1</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.metatype.annotations</artifactId>
- <version>1.4.1</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.util.promise</artifactId>
- <version>1.3.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.util.function</artifactId>
- <version>1.2.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.servlet</artifactId>
- <version>3.0.0-SNAPSHOT</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.healthcheck.api</artifactId>
- <version>2.0.4</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <version>1.7.6</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>jakarta.servlet</groupId>
- <artifactId>jakarta.servlet-api</artifactId>
- <version>6.0.0</version>
- <scope>provided</scope>
- <!-- servlet api optional: only add configs for servlets/filters your setup includes the servlet runtime! -->
- <optional>true</optional>
- </dependency>
- <dependency>
- <groupId>org.apache.servicemix.bundles</groupId>
- <artifactId>org.apache.servicemix.bundles.quartz</artifactId>
- <version>2.3.2_1</version>
- <scope>provided</scope>
- <!-- if not present in runtime, there is no support for async health checks -->
- <optional>true</optional>
- </dependency>
-
- <!-- used for JSONWriter, included in bundle via Conditional-Package -->
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.utils</artifactId>
- <version>1.11.8</version>
- <scope>provided</scope>
- </dependency>
-
- <!-- START test scope dependencies -->
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.13.2</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>3.11.2</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-simple</artifactId>
- <version>1.7.6</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-container-forked</artifactId>
- <version>${pax-exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-junit4</artifactId>
- <version>${pax-exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.exam</groupId>
- <artifactId>pax-exam-link-mvn</artifactId>
- <version>${pax-exam.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.url</groupId>
- <artifactId>pax-url-aether</artifactId>
- <version>${pax-link.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.ops4j.pax.url</groupId>
- <artifactId>pax-url-wrap</artifactId>
- <version>${pax-link.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.geronimo.specs</groupId>
- <artifactId>geronimo-atinject_1.0_spec</artifactId>
- <version>1.0</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.framework</artifactId>
- <version>7.0.5</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.http.servlet-api</artifactId>
- <version>4.0.0-SNAPSHOT</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.http.jetty</artifactId>
- <version>6.0.0-SNAPSHOT</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.http.sslfilter</artifactId>
- <version>4.0.0-SNAPSHOT</version>
- <scope>test</scope>
- </dependency>
-
- <!-- END test scope dependencies -->
-
- </dependencies>
-</project>
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeHealthCheck.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeHealthCheck.java
deleted file mode 100644
index 80712502da..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeHealthCheck.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.felix.hc.api.FormattingResultLog;
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.Result.Status;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckExecutor;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.util.HealthCheckFilter;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.ComponentConstants;
-import org.osgi.service.component.ComponentContext;
-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.metatype.annotations.Designate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** {@link HealthCheck} that executes a number of other HealthChecks, selected by their tags, and merges their Results. */
-
-@Component(service = HealthCheck.class, configurationPolicy = ConfigurationPolicy.REQUIRE)
-@Designate(ocd = CompositeHealthCheckConfiguration.class, factory = true)
-public class CompositeHealthCheck implements HealthCheck {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- static final String PROP_FILTER_TAGS = "filter.tags";
-
- private String[] filterTags;
-
- private boolean combineTagsWithOr;
-
- @Reference
- private HealthCheckExecutor healthCheckExecutor;
-
- private BundleContext bundleContext;
- private HealthCheckFilter healthCheckFilter;
-
- private volatile ComponentContext componentContext;
-
- @Activate
- protected void activate(final CompositeHealthCheckConfiguration configuration, final ComponentContext ctx) {
- bundleContext = ctx.getBundleContext();
- componentContext = ctx;
- healthCheckFilter = new HealthCheckFilter(bundleContext);
-
- filterTags = configuration.filter_tags();
- combineTagsWithOr = configuration.filter_combineTagsWithOr();
- log.debug("Activated, will select HealthCheck having tags {} {}", Arrays.asList(filterTags),
- combineTagsWithOr ? "using OR" : "using AND");
- }
-
- @Deactivate
- protected void deactivate() {
- bundleContext = null;
- healthCheckFilter = null;
- componentContext = null;
- }
-
- @Override
- public Result execute() {
- final ComponentContext localCtx = this.componentContext;
- final ServiceReference referenceToThis = localCtx == null ? null : localCtx.getServiceReference();
- Result result = referenceToThis == null ? null : checkForRecursion(referenceToThis, new HashSet<String>());
- if (result != null) {
- // return recursion error
- return result;
- }
-
- FormattingResultLog resultLog = new FormattingResultLog();
- HealthCheckExecutionOptions options = new HealthCheckExecutionOptions();
- options.setCombineTagsWithOr(combineTagsWithOr);
- List<HealthCheckExecutionResult> executionResults = healthCheckExecutor.execute(HealthCheckSelector.tags(filterTags), options);
- resultLog.debug("Executing {} HealthChecks selected by tags {}", executionResults.size(), Arrays.asList(filterTags));
- result = new CompositeResult(resultLog, executionResults);
-
- return result;
- }
-
- Result checkForRecursion(ServiceReference hcReference, Set<String> alreadyBannedTags) {
-
- HealthCheckMetadata thisCheckMetadata = new HealthCheckMetadata(hcReference);
-
- Set<String> bannedTagsForThisCompositeCheck = new HashSet<String>();
- bannedTagsForThisCompositeCheck.addAll(alreadyBannedTags);
- bannedTagsForThisCompositeCheck.addAll(thisCheckMetadata.getTags());
-
- String[] tagsForIncludedChecksArr = toStringArray(hcReference.getProperty(PROP_FILTER_TAGS), new String[0]);
- Set<String> tagsForIncludedChecks = new HashSet<String>(Arrays.asList(tagsForIncludedChecksArr));
-
- log.debug("HC {} has banned tags {}", thisCheckMetadata.getName(), bannedTagsForThisCompositeCheck);
- log.debug("tagsForIncludedChecks {}", tagsForIncludedChecks);
-
- // is this HC ok?
- Set<String> intersection = new HashSet<String>();
- intersection.addAll(bannedTagsForThisCompositeCheck);
- intersection.retainAll(tagsForIncludedChecks);
-
- if (!intersection.isEmpty()) {
- return new Result(Status.HEALTH_CHECK_ERROR,
- "INVALID CONFIGURATION: Cycle detected in composite health check hierarchy. Health check '"
- + thisCheckMetadata.getName()
- + "' (" + hcReference.getProperty(Constants.SERVICE_ID) + ") must not have tag(s) " + intersection
- + " as a composite check in the hierarchy is itself already tagged alike (tags assigned to composite checks: "
- + bannedTagsForThisCompositeCheck + ")");
- }
-
- // check each sub composite check
- ServiceReference[] hcRefsOfCompositeCheck = healthCheckFilter
- .getHealthCheckServiceReferences(HealthCheckSelector.tags(tagsForIncludedChecksArr), combineTagsWithOr);
- for (ServiceReference hcRefOfCompositeCheck : hcRefsOfCompositeCheck) {
- if (CompositeHealthCheck.class.getName().equals(hcRefOfCompositeCheck.getProperty(ComponentConstants.COMPONENT_NAME))) {
- log.debug("Checking sub composite HC {}, {}", hcRefOfCompositeCheck,
- hcRefOfCompositeCheck.getProperty(ComponentConstants.COMPONENT_NAME));
- Result result = checkForRecursion(hcRefOfCompositeCheck, bannedTagsForThisCompositeCheck);
- if (result != null) {
- // found recursion
- return result;
- }
- }
-
- }
-
- // no recursion detected
- return null;
-
- }
-
- public static String[] toStringArray(Object propValue, String[] defaultArray) {
- if (propValue instanceof String[]) {
- return (String[]) propValue;
- } else if (propValue instanceof String) {
- return new String[] { (String) propValue };
- } else if (propValue == null) {
- return defaultArray;
- }
- return defaultArray;
- }
-
- void setHealthCheckFilter(HealthCheckFilter healthCheckFilter) {
- this.healthCheckFilter = healthCheckFilter;
- }
-
- void setFilterTags(String[] filterTags) {
- this.filterTags = filterTags;
- }
-
- void setCombineTagsWithOr(boolean combineTagsWithOr) {
- this.combineTagsWithOr = combineTagsWithOr;
- }
-
- void setHealthCheckExecutor(HealthCheckExecutor healthCheckExecutor) {
- this.healthCheckExecutor = healthCheckExecutor;
- }
-
- void setComponentContext(ComponentContext ctx) {
- this.componentContext = ctx;
- }
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeHealthCheckConfiguration.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeHealthCheckConfiguration.java
deleted file mode 100644
index 32760b1371..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeHealthCheckConfiguration.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl;
-
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@ObjectClassDefinition(name = "Apache Felix Composite Health Check", description = "Executes a set of health checks, selected by tags.")
-@interface CompositeHealthCheckConfiguration {
-
- @AttributeDefinition(name = "Name", description = "Name of this health check.")
- String hc_name() default "";
-
- @AttributeDefinition(name = "Tags", description = "List of tags for this health check, used to select subsets of health checks for execution e.g. by a composite health check.")
- String[] hc_tags() default {};
-
- @AttributeDefinition(name = "MBean Name", description = "Name of the MBean to create for this health check. If empty, no MBean is registered.")
- String hc_mbean_name() default "";
-
- //
-
- @AttributeDefinition(name = "Filter Tags", description = "Tags used to select which health checks the composite health check executes.")
- String[] filter_tags() default {};
-
- @AttributeDefinition(name = "Combine Tags With Or", description = "Tags used to select which health checks the composite health check executes.")
- boolean filter_combineTagsWithOr() default false;
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeResult.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeResult.java
deleted file mode 100644
index 847101cd5e..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/CompositeResult.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl;
-
-import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
-
-import java.util.List;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.ResultLog.Entry;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-
-public class CompositeResult extends Result {
-
- public CompositeResult(ResultLog log, List<HealthCheckExecutionResult> executionResults) {
- super(log);
-
- for (HealthCheckExecutionResult executionResult : executionResults) {
- HealthCheckMetadata healthCheckMetadata = executionResult.getHealthCheckMetadata();
- Result healthCheckResult = executionResult.getHealthCheckResult();
- for (Entry entry : healthCheckResult) {
- resultLog.add(new ResultLog.Entry(entry.getStatus(), healthCheckMetadata.getName() + ": " + entry.getMessage(),
- entry.getException()));
- }
- resultLog.add(new ResultLog.Entry(healthCheckMetadata.getName() + " finished after "
- + msHumanReadable(executionResult.getElapsedTimeInMs()) + (executionResult.hasTimedOut() ? " (timed out)" : ""), true));
- }
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/JmxAdjustableStatusHealthCheck.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/JmxAdjustableStatusHealthCheck.java
deleted file mode 100644
index c9895e324a..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/JmxAdjustableStatusHealthCheck.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-
-import javax.management.Attribute;
-import javax.management.AttributeList;
-import javax.management.AttributeNotFoundException;
-import javax.management.DynamicMBean;
-import javax.management.InvalidAttributeValueException;
-import javax.management.MBeanAttributeInfo;
-import javax.management.MBeanException;
-import javax.management.MBeanInfo;
-import javax.management.MBeanOperationInfo;
-import javax.management.MBeanParameterInfo;
-import javax.management.ReflectionException;
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.Result.Status;
-import org.apache.felix.hc.core.impl.util.AdhocStatusHealthCheck;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.component.ComponentContext;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Allows to dynamically add a health check that returns WARN or CRITICAL for certain tags for testing purposes or go-live sequences. Uses an MBean to
- * add/remove the DynamicTestingHealthCheck dynamically. */
-@Component
-public class JmxAdjustableStatusHealthCheck {
- private static final Logger LOG = LoggerFactory.getLogger(JmxAdjustableStatusHealthCheck.class);
-
- public static final String OBJECT_NAME = "org.apache.felix.healthcheck:type=AdjustableStatusHealthCheck";
-
- private BundleContext bundleContext;
-
- private ServiceRegistration mbeanRegistration = null;
- private ServiceRegistration healthCheckRegistration = null;
-
- @Activate
- protected final void activate(final ComponentContext context) {
- this.bundleContext = context.getBundleContext();
- registerMbean();
- }
-
- @Deactivate
- protected final void deactivate(final ComponentContext context) {
- unregisterMbean();
- unregisterDynamicHealthCheck();
- }
-
- private void registerMbean() {
- final Dictionary<String, String> mbeanProps = new Hashtable<String, String>();
- mbeanProps.put("jmx.objectname", OBJECT_NAME);
- AdjustableHealthCheckStatusMBean adjustableHealthCheckStatusMBean = new AdjustableHealthCheckStatusMBean();
- this.mbeanRegistration = bundleContext.registerService(DynamicMBean.class.getName(), adjustableHealthCheckStatusMBean, mbeanProps);
- LOG.debug("Registered mbean {} as {}", adjustableHealthCheckStatusMBean, OBJECT_NAME);
- }
-
- private void unregisterMbean() {
- if (this.mbeanRegistration != null) {
- this.mbeanRegistration.unregister();
- this.mbeanRegistration = null;
- LOG.debug("Unregistered mbean AdjustableHealthCheckStatusMBean");
- }
- }
-
- /* synchronized as potentially multiple users can run JMX operations */
- private synchronized void registerDynamicHealthCheck(Result.Status status, String[] tags) {
- unregisterDynamicHealthCheck();
- HealthCheck healthCheck = new AdhocStatusHealthCheck(status, "Set dynamically via JMX bean " + JmxAdjustableStatusHealthCheck.OBJECT_NAME);
- Dictionary<String, Object> props = new Hashtable<String, Object>();
- props.put(HealthCheck.NAME, "JMX Adhoc Result");
- props.put(HealthCheck.TAGS, tags);
-
- healthCheckRegistration = bundleContext.registerService(HealthCheck.class.getName(), healthCheck, props);
- }
-
- /* synchronized as potentially multiple users can run JMX operations */
- private synchronized void unregisterDynamicHealthCheck() {
- if (this.healthCheckRegistration != null) {
- this.healthCheckRegistration.unregister();
- this.healthCheckRegistration = null;
- LOG.debug("Unregistered DynamicTestingHealthCheck");
- }
- }
-
- private class AdjustableHealthCheckStatusMBean implements DynamicMBean {
-
- private static final String OP_RESET = "reset";
- private static final String OP_ADD_WARN_RESULT_FOR_TAGS = "addWarnResultForTags";
- private static final String OP_ADD_TEMPORARILY_UNAVAILABLE_RESULT_FOR_TAGS = "addTemporarilyUnavailableResultForTags";
- private static final String OP_ADD_CRITICAL_RESULT_FOR_TAGS = "addCriticalResultForTags";
-
- private static final String ATT_TAGS = "tags";
- private static final String ATT_STATUS = "status";
-
- private static final String STATUS_INACTIVE = "INACTIVE";
-
- /** The mbean info. */
- private final MBeanInfo mbeanInfo;
-
- private List<String> tags = new ArrayList<String>();
- private String status = STATUS_INACTIVE;
-
- public AdjustableHealthCheckStatusMBean() {
- this.mbeanInfo = this.createMBeanInfo();
- }
-
- @Override
- public Object getAttribute(final String attribute) throws AttributeNotFoundException, MBeanException, ReflectionException {
-
- if (ATT_TAGS.equals(attribute)) {
- return String.join(",", tags);
- } else if (ATT_STATUS.equals(attribute)) {
- return status.toString();
- } else {
- throw new AttributeNotFoundException("Attribute " + attribute + " not found.");
- }
- }
-
- @Override
- public AttributeList getAttributes(final String[] attributes) {
- final AttributeList result = new AttributeList();
- for (String att : attributes) {
- try {
- result.add(new Attribute(att, getAttribute(att)));
- } catch (Exception e) {
- throw new IllegalArgumentException(e.getMessage(), e);
- }
- }
- return result;
- }
-
- private MBeanInfo createMBeanInfo() {
- final List<MBeanAttributeInfo> attrs = new ArrayList<MBeanAttributeInfo>();
-
- attrs.add(new MBeanAttributeInfo(ATT_TAGS, String.class.getName(), "Tags", true, false, false));
- attrs.add(new MBeanAttributeInfo(ATT_STATUS, String.class.getName(), "Status", true, false, false));
-
- MBeanParameterInfo[] params = new MBeanParameterInfo[] {
- new MBeanParameterInfo(ATT_TAGS, "java.lang.String", "Comma separated list of tags") };
-
- final List<MBeanOperationInfo> ops = new ArrayList<MBeanOperationInfo>();
- ops.add(new MBeanOperationInfo(OP_RESET, "Resets this testing mechanism and removes the failing HC",
- new MBeanParameterInfo[0], "java.lang.String", MBeanOperationInfo.ACTION));
- ops.add(new MBeanOperationInfo(OP_ADD_TEMPORARILY_UNAVAILABLE_RESULT_FOR_TAGS, "Adds a TEMPORARILY_UNAVAILABLE result for the given tags",
- params, "java.lang.String", MBeanOperationInfo.ACTION));
- ops.add(new MBeanOperationInfo(OP_ADD_CRITICAL_RESULT_FOR_TAGS, "Adds a CRITICAL result for the given tags",
- params, "java.lang.String", MBeanOperationInfo.ACTION));
- ops.add(new MBeanOperationInfo(OP_ADD_WARN_RESULT_FOR_TAGS, "Adds a WARN result for the given tags",
- params, "java.lang.String", MBeanOperationInfo.ACTION));
-
- return new MBeanInfo(this.getClass().getName(),
- "Adjustable Health Check", attrs.toArray(new MBeanAttributeInfo[attrs.size()]), null,
- ops.toArray(new MBeanOperationInfo[ops.size()]), null);
- }
-
- @Override
- public MBeanInfo getMBeanInfo() {
- return this.mbeanInfo;
- }
-
- @Override
- public Object invoke(final String actionName, final Object[] params, final String[] signature)
- throws MBeanException, ReflectionException {
-
- Status newStatus = null;
-
- tags = params.length > 0 ? Arrays.asList(params[0].toString().split("[,; ]+")) : Arrays.asList("");
-
- if (OP_RESET.equals(actionName)) {
- unregisterDynamicHealthCheck();
- LOG.info("JMX-adjustable Health Check was reset");
- status = STATUS_INACTIVE;
- return "Reset successful";
- } else if (OP_ADD_TEMPORARILY_UNAVAILABLE_RESULT_FOR_TAGS.equals(actionName)) {
- newStatus = Result.Status.TEMPORARILY_UNAVAILABLE;
- } else if (OP_ADD_CRITICAL_RESULT_FOR_TAGS.equals(actionName)) {
- newStatus = Result.Status.CRITICAL;
- } else if (OP_ADD_WARN_RESULT_FOR_TAGS.equals(actionName)) {
- newStatus = Result.Status.WARN;
- } else {
- throw new MBeanException(
- new UnsupportedOperationException(getClass().getSimpleName() + " does not support operation " + actionName));
- }
-
- status = newStatus.toString();
- registerDynamicHealthCheck(newStatus, tags.toArray(new String[tags.size()]));
- LOG.info("Activated JMX-adjustable Health Check with status "+newStatus+" and tags " + String.join(",", tags));
- return "Added check with result "+newStatus;
-
- }
-
- @Override
- public void setAttribute(final Attribute attribute)
- throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException,
- ReflectionException {
- throw new MBeanException(
- new UnsupportedOperationException(getClass().getSimpleName() + " does not support setting attributes."));
- }
-
- @Override
- public AttributeList setAttributes(final AttributeList attributes) {
- return new AttributeList();
- }
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/commands/HealthCheckExecCommand.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/commands/HealthCheckExecCommand.java
deleted file mode 100644
index 2b7b1e9b7d..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/commands/HealthCheckExecCommand.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.hc.core.impl.commands;
-
-import java.util.List;
-
-import org.apache.felix.hc.api.FormattingResultLog;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckExecutor;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.CompositeResult;
-import org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializer;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-
-@Component(
- service = HealthCheckExecCommand.class,
- property = {
- "osgi.command.scope=hc",
- "osgi.command.function=exec"
- }
- )
-public class HealthCheckExecCommand {
-
- @Reference
- HealthCheckExecutor healthCheckExecutor;
-
- @Reference
- ResultTxtVerboseSerializer resultTxtVerboseSerializer;
-
- public String exec(String... params) {
-
- boolean isDebug = false;
- boolean combineWithOr = true;
- boolean isName = false;
- HealthCheckSelector selector = HealthCheckSelector.empty();
- for (String param : params) {
- if(param.startsWith("-")) {
- if("-v".equals(param)) {
- isDebug = true;
- } else if("-a".equals(param)) {
- combineWithOr = false;
- } else if("-n".equals(param)) {
- isName = true;
- } else if("-h".equals(param)) {
- return getHelpText();
- } else {
- System.out.println("unrecognized option: "+param);
- }
- } else {
- if(isName) {
- selector = selector.withNames(param.split(","));
- } else {
- selector = selector.withTags(param.split(","));
- }
- }
- }
-
- boolean defaultTagUsed = selector.tags() == null && selector.names() == null;
-
- HealthCheckExecutionOptions options = new HealthCheckExecutionOptions();
- options.setCombineTagsWithOr(combineWithOr);
- List<HealthCheckExecutionResult> executionResult = healthCheckExecutor.execute(selector, options);
- String result = resultTxtVerboseSerializer.serialize(new CompositeResult(new FormattingResultLog(), executionResult), executionResult, isDebug);
- return (defaultTagUsed ? "Configured default tag used for execution\n" : "") + result;
- }
-
- private String getHelpText() {
- return "Usage:\n hc:exec [-v] [-a] [tag1,tag2] [-n \"Name 1,Name 2\"]\n -v verbose/debug\n -a combine tags with 'and' logic\n -n name(s) to be executed";
- }
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/commands/HealthCheckListCommand.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/commands/HealthCheckListCommand.java
deleted file mode 100644
index 407153d70a..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/commands/HealthCheckListCommand.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.hc.core.impl.commands;
-
-import static org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializer.rightPad;
-
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.util.HealthCheckFilter;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-
-@Component(
- service = HealthCheckListCommand.class,
- property = {
- "osgi.command.scope=hc",
- "osgi.command.function=list"
- }
- )
-public class HealthCheckListCommand {
-
- private BundleContext bundleContext;
-
- @Activate
- protected final void activate(final BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
- public String list(String... params) {
-
- boolean isVerbose = false;
- for (String param : params) {
- if("-v".equals(param)) {
- isVerbose = true;
- } else if("-h".equals(param)) {
- return getHelpText();
- } else {
- System.out.println("unrecognized option: "+param);
- return getHelpText();
- }
- }
-
- HealthCheckFilter hcFilter = new HealthCheckFilter(bundleContext);
- HealthCheckSelector selector = HealthCheckSelector.empty();
-
- ServiceReference<HealthCheck>[] hcRefs = hcFilter.getHealthCheckServiceReferences(selector);
- Stream<ServiceReference<HealthCheck>> hcRefsStream = Arrays.asList(hcRefs).stream();
-
- if(isVerbose) {
-
- return hcRefsStream
- .map(ref -> {
- HealthCheckMetadata metadata = new HealthCheckMetadata(ref);
- return String.join(" ",
- rightPad(metadata.getTitle(), 60),
- rightPad(String.join(",", metadata.getTags()), 50),
- ref.getBundle().getSymbolicName());
- })
- .collect(Collectors.joining("\n"));
-
- } else {
-
- return hcRefsStream
- .flatMap(ref -> { return new HealthCheckMetadata(ref).getTags().stream(); })
- .distinct()
- .sorted(Comparator.naturalOrder())
- .collect(Collectors.joining(","));
-
- }
- }
-
- private String getHelpText() {
- return "Usage: hc:list [-v]\n -v verbose";
- }
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/CombinedExecutionResult.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/CombinedExecutionResult.java
deleted file mode 100644
index d864b1ec41..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/CombinedExecutionResult.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.Result.Status;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-
-/** Used to group execution results. */
-public class CombinedExecutionResult implements HealthCheckExecutionResult {
-
- final List<HealthCheckExecutionResult> executionResults;
- final Result overallResult;
-
- public CombinedExecutionResult(List<HealthCheckExecutionResult> executionResults) {
- this(executionResults, Result.Status.OK);
- }
- public CombinedExecutionResult(List<HealthCheckExecutionResult> executionResults, Result.Status statusForZeroResult) {
- this.executionResults = Collections.unmodifiableList(executionResults);
- Result.Status mostSevereStatus = executionResults.isEmpty() ? statusForZeroResult : Result.Status.OK;
- for (HealthCheckExecutionResult executionResult : executionResults) {
- Status status = executionResult.getHealthCheckResult().getStatus();
- if (status.ordinal() > mostSevereStatus.ordinal()) {
- mostSevereStatus = status;
- }
- }
- overallResult = new Result(mostSevereStatus, "Overall status " + mostSevereStatus);
- }
-
- @Override
- public Result getHealthCheckResult() {
- return overallResult;
- }
-
- public List<HealthCheckExecutionResult> getExecutionResults() {
- return executionResults;
- }
- @Override
- public long getElapsedTimeInMs() {
- long maxElapsed = 0;
- for(HealthCheckExecutionResult result: executionResults) {
- maxElapsed = Math.max(maxElapsed, result.getElapsedTimeInMs());
- }
- return maxElapsed;
- }
-
- @Override
- public Date getFinishedAt() {
- Date latestFinishedAt = null;
- for(HealthCheckExecutionResult result: executionResults) {
- if(latestFinishedAt == null || latestFinishedAt.before(result.getFinishedAt())) {
- latestFinishedAt = result.getFinishedAt();
- }
- }
- return latestFinishedAt;
- }
-
- @Override
- public boolean hasTimedOut() {
- for(HealthCheckExecutionResult result: executionResults) {
- if(result.hasTimedOut()) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public HealthCheckMetadata getHealthCheckMetadata() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String toString() {
- return "CombinedExecutionResult [size="+executionResults.size()+" overall status=" + getHealthCheckResult().getStatus() +
- ", finishedAt=" + getFinishedAt() +
- ", elapsedTimeInMs=" + getElapsedTimeInMs() +
- ", timedOut=" + hasTimedOut() +
- "]";
- }
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/ExecutionResult.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/ExecutionResult.java
deleted file mode 100644
index b14759ded1..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/ExecutionResult.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import java.text.Collator;
-import java.util.Date;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-
-/** The result of executing a {@link HealthCheck}. */
-public class ExecutionResult implements Comparable<ExecutionResult>, HealthCheckExecutionResult {
-
- private final Result resultFromHC;
-
- private final HealthCheckMetadata metaData;
-
- private final Date finishedAt;
-
- private final long elapsedTimeInMs;
-
- private final boolean timedOut;
-
- public ExecutionResult(final HealthCheckMetadata metadata,
- final Result simpleResult,
- final long elapsedTimeInMs,
- final boolean timedout) {
- this(metadata, simpleResult, new Date(), elapsedTimeInMs, timedout);
- }
-
- /** Full constructor */
- public ExecutionResult(final HealthCheckMetadata metadata,
- final Result simpleResult,
- final Date finishedAt,
- final long elapsedTimeInMs,
- final boolean timedout) {
- this.metaData = metadata;
- this.resultFromHC = simpleResult;
- this.finishedAt = finishedAt;
- this.elapsedTimeInMs = elapsedTimeInMs;
- this.timedOut = timedout;
- }
-
- /** Shortcut constructor for a result */
- public ExecutionResult(final HealthCheckMetadata metadata,
- final Result simpleResult,
- final long elapsedTimeInMs) {
- this(metadata, simpleResult, elapsedTimeInMs, false);
- }
-
- /** Shortcut constructor to create error/timed out result. */
- public ExecutionResult(final HealthCheckMetadata metadata,
- final Result.Status status,
- final String errorMessage,
- final long elapsedTime, boolean timedOut) {
- this(metadata, new Result(status, errorMessage), elapsedTime, timedOut);
- }
-
- @Override
- public Result getHealthCheckResult() {
- return this.resultFromHC;
- }
-
- @Override
- public String toString() {
- return "ExecutionResult [status=" + this.resultFromHC.getStatus() +
- ", finishedAt=" + finishedAt +
- ", elapsedTimeInMs=" + elapsedTimeInMs +
- ", timedOut=" + timedOut +
- "]";
- }
-
- @Override
- public long getElapsedTimeInMs() {
- return elapsedTimeInMs;
- }
-
- @Override
- public HealthCheckMetadata getHealthCheckMetadata() {
- return this.metaData;
- }
-
- @Override
- public Date getFinishedAt() {
- return finishedAt;
- }
-
- @Override
- public boolean hasTimedOut() {
- return this.timedOut;
- }
-
- /** Natural order of results (failed results are sorted before ok results). */
- @Override
- public int compareTo(ExecutionResult otherResult) {
- int retVal = otherResult.getHealthCheckResult().getStatus().compareTo(this.getHealthCheckResult().getStatus());
- if (retVal == 0) {
- retVal = Collator.getInstance().compare(this.getHealthCheckMetadata().getTitle(),
- otherResult.getHealthCheckMetadata().getTitle());
- }
- return retVal;
- }
-
- long getServiceId() {
- return this.metaData.getServiceId();
- }
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/ExtendedHealthCheckExecutor.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/ExtendedHealthCheckExecutor.java
deleted file mode 100644
index 0922e30a73..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/ExtendedHealthCheckExecutor.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import java.util.List;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckExecutor;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.osgi.framework.ServiceReference;
-
-/** Internal service used by the JMX and ServiceUnavailableFilter */
-public interface ExtendedHealthCheckExecutor extends HealthCheckExecutor {
-
- /** execute single health check using cache, used by JMX */
- HealthCheckExecutionResult execute(ServiceReference<HealthCheck> ref);
-
- /** internal interface to retrieve service references */
- ServiceReference<HealthCheck>[] selectHealthCheckReferences(HealthCheckSelector selector, HealthCheckExecutionOptions options);
-
- /** internal interface to execute checks for service references */
- List<HealthCheckExecutionResult> execute(final ServiceReference<HealthCheck>[] healthCheckReferences, HealthCheckExecutionOptions options);
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImpl.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImpl.java
deleted file mode 100644
index 9702626742..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImpl.java
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
-import static org.apache.felix.hc.core.impl.executor.HealthCheckExecutorImplConfiguration.LONGRUNNING_FUTURE_THRESHOLD_CRITICAL_DEFAULT_MS;
-import static org.apache.felix.hc.core.impl.executor.HealthCheckExecutorImplConfiguration.RESULT_CACHE_TTL_DEFAULT_MS;
-import static org.apache.felix.hc.core.impl.executor.HealthCheckExecutorImplConfiguration.TIMEOUT_DEFAULT_MS;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.felix.hc.api.FormattingResultLog;
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckExecutor;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.executor.async.AsyncHealthCheckExecutor;
-import org.apache.felix.hc.core.impl.util.HealthCheckFilter;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.startlevel.FrameworkStartLevel;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Modified;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.metatype.annotations.Designate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Runs health checks for a given list of tags in parallel. */
-@Component(service = { HealthCheckExecutor.class, ExtendedHealthCheckExecutor.class }, immediate = true // immediate = true to keep the
- // cache!
-)
-@Designate(ocd = HealthCheckExecutorImplConfiguration.class)
-public class HealthCheckExecutorImpl implements ExtendedHealthCheckExecutor, ServiceListener {
-
- private final Logger logger = LoggerFactory.getLogger(this.getClass());
-
- private static final String HC_LOGGING_SYS_PROP = "org.apache.felix.hc.autoLogging";
-
- private long timeoutInMs;
-
- private long longRunningFutureThresholdForRedMs;
-
- private long resultCacheTtlInMs;
-
- private String[] defaultTags;
-
- private HealthCheckResultCache healthCheckResultCache = new HealthCheckResultCache();
-
- private TempUnavailableGracePeriodEvaluator tempUnavailableGracePeriodEvaluator;
-
- private final Map<HealthCheckMetadata, HealthCheckFuture> stillRunningFutures = new HashMap<HealthCheckMetadata, HealthCheckFuture>();
-
- @Reference
- private AsyncHealthCheckExecutor asyncHealthCheckExecutor;
-
- @Reference
- HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
-
- private BundleContext bundleContext;
-
- @Activate
- protected final void activate(final HealthCheckExecutorImplConfiguration configuration, final BundleContext bundleContext) {
- this.bundleContext = bundleContext;
-
- configure(configuration);
-
- try {
- this.bundleContext.addServiceListener(this, "("
- + Constants.OBJECTCLASS + "=" + HealthCheck.class.getName() + ")");
- } catch (final InvalidSyntaxException ise) {
- // this should really never happen as the expression above is constant
- throw new RuntimeException("Unexpected problem with filter syntax", ise);
- }
-
- logger.info("HealthCheckExecutor active at start level {}", getCurrentStartLevel());
- }
-
- @Modified
- protected final void modified(final HealthCheckExecutorImplConfiguration configuration) {
- configure(configuration);
- }
-
- @Deactivate
- protected final void deactivate() {
- this.bundleContext.removeServiceListener(this);
- this.healthCheckResultCache.clear();
- logger.info("HealthCheckExecutor shutdown at start level {}", getCurrentStartLevel());
- }
-
- private int getCurrentStartLevel() {
- return bundleContext.getBundle(Constants.SYSTEM_BUNDLE_ID).adapt(FrameworkStartLevel.class).getStartLevel();
- }
-
- protected final void configure(final HealthCheckExecutorImplConfiguration configuration) {
- this.timeoutInMs = configuration.timeoutInMs();
- if (this.timeoutInMs <= 0L) {
- this.timeoutInMs = TIMEOUT_DEFAULT_MS;
- }
-
- this.longRunningFutureThresholdForRedMs = configuration.longRunningFutureThresholdForCriticalMs();
- if (this.longRunningFutureThresholdForRedMs <= 0L) {
- this.longRunningFutureThresholdForRedMs = LONGRUNNING_FUTURE_THRESHOLD_CRITICAL_DEFAULT_MS;
- }
-
- this.resultCacheTtlInMs = configuration.resultCacheTtlInMs();
- if (this.resultCacheTtlInMs <= 0L) {
- this.resultCacheTtlInMs = RESULT_CACHE_TTL_DEFAULT_MS;
- }
-
- this.defaultTags = configuration.defaultTags();
-
- tempUnavailableGracePeriodEvaluator = new TempUnavailableGracePeriodEvaluator(configuration.temporarilyAvailableGracePeriodInMs());
-
- System.setProperty(HC_LOGGING_SYS_PROP, String.valueOf(configuration.autoLogging()));
-
- }
-
- @Override
- public void serviceChanged(final ServiceEvent event) {
- if (event.getType() == ServiceEvent.UNREGISTERING) {
- final Long serviceId = (Long) event.getServiceReference().getProperty(Constants.SERVICE_ID);
- this.healthCheckResultCache.removeCachedResult(serviceId);
- }
- }
-
- @Override
- public List<HealthCheckExecutionResult> execute(HealthCheckSelector selector) {
- return execute(selector, new HealthCheckExecutionOptions());
- }
-
- @Override
- public List<HealthCheckExecutionResult> execute(HealthCheckSelector selector, HealthCheckExecutionOptions options) {
- logger.debug("Starting executing checks for filter selector {} and execution options {}", selector, options);
-
- if ((selector.names() == null || selector.names().length == 0) && (selector.tags() == null || selector.tags().length == 0)) {
- logger.debug("Using default tags");
- selector.withTags(defaultTags);
- }
-
- final ServiceReference<HealthCheck>[] healthCheckReferences = selectHealthCheckReferences(selector, options);
- List<HealthCheckExecutionResult> results = this.execute(healthCheckReferences, options);
- return results;
-
- }
-
- /** @see org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor#selectHealthCheckReferences(HealthCheckSelector,
- * HealthCheckExecutionOptions) */
- @Override
- public ServiceReference<HealthCheck>[] selectHealthCheckReferences(HealthCheckSelector selector, HealthCheckExecutionOptions options) {
- final HealthCheckFilter filter = new HealthCheckFilter(this.bundleContext);
- final ServiceReference<HealthCheck>[] healthCheckReferences = filter.getHealthCheckServiceReferences(selector, options.isCombineTagsWithOr());
- return healthCheckReferences;
- }
-
- /** @see org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor#execute(org.osgi.framework.ServiceReference) */
- @Override
- public HealthCheckExecutionResult execute(final ServiceReference<HealthCheck> ref) {
- final HealthCheckMetadata metadata = this.getHealthCheckMetadata(ref);
- return createResultsForDescriptor(metadata);
- }
-
- /** @see org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor#execute(ServiceReference[], HealthCheckExecutionOptions) */
- @Override
- public List<HealthCheckExecutionResult> execute(final ServiceReference<HealthCheck>[] healthCheckReferences,
- HealthCheckExecutionOptions options) {
- final long startTime = System.currentTimeMillis();
-
- final List<HealthCheckExecutionResult> results = new ArrayList<HealthCheckExecutionResult>();
- final List<HealthCheckMetadata> healthCheckDescriptors = getHealthCheckMetadata(healthCheckReferences);
-
- createResultsForDescriptors(healthCheckDescriptors, results, options);
-
- if (logger.isDebugEnabled()) {
- logger.debug("Time consumed for all checks: {}", msHumanReadable(System.currentTimeMillis() - startTime));
- }
-
- // sort result
- Collections.sort(results, new Comparator<HealthCheckExecutionResult>() {
-
- @Override
- public int compare(final HealthCheckExecutionResult arg0,
- final HealthCheckExecutionResult arg1) {
- return ((ExecutionResult) arg0).compareTo((ExecutionResult) arg1);
- }
-
- });
- return results;
- }
-
- // method to get the result for one HC (using the generic method to get multiple under the hood
- private HealthCheckExecutionResult createResultsForDescriptor(final HealthCheckMetadata metadata) {
-
- final List<HealthCheckExecutionResult> results = new ArrayList<HealthCheckExecutionResult>();
- final List<HealthCheckMetadata> healthCheckDescriptors = new ArrayList<HealthCheckMetadata>();
- healthCheckDescriptors.add(metadata);
-
- createResultsForDescriptors(healthCheckDescriptors, results, new HealthCheckExecutionOptions());
-
- if (results.size() != 1) {
- throw new IllegalStateException("Execute method for a single service reference unexpectedly resulted in "+results.size()+ " results: "+results);
- }
- return results.get(0);
-
- }
-
- private void createResultsForDescriptors(final List<HealthCheckMetadata> healthCheckDescriptors,
- final List<HealthCheckExecutionResult> results, HealthCheckExecutionOptions options) {
- // -- All methods below check if they can transform a healthCheckDescriptor into a result
- // -- if yes the descriptor is removed from the list and the result added
-
- // get async results
- if (!options.isForceInstantExecution()) {
- if (asyncHealthCheckExecutor != null) {
- asyncHealthCheckExecutor.collectAsyncResults(healthCheckDescriptors, results, healthCheckResultCache);
- }
- }
-
- // reuse cached results where possible
- if (!options.isForceInstantExecution()) {
- healthCheckResultCache.useValidCacheResults(healthCheckDescriptors, results, resultCacheTtlInMs);
- }
-
- // everything else is executed in parallel via futures
- List<HealthCheckFuture> futures = createOrReuseFutures(healthCheckDescriptors);
-
- // wait for futures at most until timeout (but will return earlier if all futures are finished)
- waitForFuturesRespectingTimeout(futures, options);
- collectResultsFromFutures(futures, results);
-
- // respect sticky results if configured via HealthCheck.KEEP_NON_OK_RESULTS_STICKY_FOR_SEC
- appendStickyResultLogIfConfigured(results);
-
- // ensure long standing TEMPORARILY_UNAVAILABLE results are marked as CRITICAL
- tempUnavailableGracePeriodEvaluator.evaluateGracePeriodForTemporarilyUnavailableResults(results);
- }
-
- private void appendStickyResultLogIfConfigured(List<HealthCheckExecutionResult> results) {
- ListIterator<HealthCheckExecutionResult> resultsIt = results.listIterator();
- while (resultsIt.hasNext()) {
- HealthCheckExecutionResult result = resultsIt.next();
- Long warningsStickForMinutes = result.getHealthCheckMetadata().getKeepNonOkResultsStickyForSec();
- if (warningsStickForMinutes != null && warningsStickForMinutes > 0) {
- result = healthCheckResultCache.createExecutionResultWithStickyResults(result);
- resultsIt.set(result);
- }
- }
- }
-
-
- /** Create the health check meta data */
- private List<HealthCheckMetadata> getHealthCheckMetadata(final ServiceReference<?>... healthCheckReferences) {
- final List<HealthCheckMetadata> descriptors = new LinkedList<HealthCheckMetadata>();
- for (final ServiceReference<?> serviceReference : healthCheckReferences) {
- final HealthCheckMetadata descriptor = getHealthCheckMetadata(serviceReference);
-
- descriptors.add(descriptor);
- }
-
- return descriptors;
- }
-
- /** Create the health check meta data */
- private HealthCheckMetadata getHealthCheckMetadata(final ServiceReference<?> healthCheckReference) {
- final HealthCheckMetadata descriptor = new HealthCheckMetadata(healthCheckReference);
- return descriptor;
- }
-
- /** Create or reuse future for the list of health checks */
- private List<HealthCheckFuture> createOrReuseFutures(final List<HealthCheckMetadata> healthCheckDescriptors) {
- final List<HealthCheckFuture> futuresForResultOfThisCall = new LinkedList<HealthCheckFuture>();
-
- synchronized (this.stillRunningFutures) {
- for (final HealthCheckMetadata md : healthCheckDescriptors) {
-
- futuresForResultOfThisCall.add(createOrReuseFuture(md));
-
- }
- }
- return futuresForResultOfThisCall;
- }
-
- /** Create or reuse future for the health check This method must be synchronized by the caller(!) on stillRunningFutures */
- private HealthCheckFuture createOrReuseFuture(final HealthCheckMetadata metadata) {
- HealthCheckFuture future = this.stillRunningFutures.get(metadata);
- if (future != null) {
- logger.debug("Found a future that is still running for {}", metadata);
- } else {
- logger.debug("Creating future for {}", metadata);
- future = new HealthCheckFuture(metadata, bundleContext, new HealthCheckFuture.Callback() {
-
- @Override
- public void finished(final HealthCheckExecutionResult result) {
- healthCheckResultCache.updateWith(result);
- asyncHealthCheckExecutor.updateWith(result);
- tempUnavailableGracePeriodEvaluator.updateTemporarilyUnavailableTimestampWith(result);
- synchronized (stillRunningFutures) {
- stillRunningFutures.remove(metadata);
- }
- }
- });
- this.stillRunningFutures.put(metadata, future);
-
- final HealthCheckFuture newFuture = future;
-
- healthCheckExecutorThreadPool.execute(new Runnable() {
- @Override
- public void run() {
- newFuture.run();
- synchronized (stillRunningFutures) {
- // notify executor threads that newFuture is finished. Wrapping it in another runnable
- // ensures that newFuture.isDone() will return true (if e.g. done in callback above, there are
- // still a few lines of code until the future is really done and hence then the executor thread
- // is sometime notified a bit too early, still receives the result isDone()=false and then waits
- // for another 50ms, even though the future was about to be done one ms later)
- stillRunningFutures.notifyAll();
- }
- }
- });
- }
-
- return future;
- }
-
- /** Wait for the futures until the timeout is reached */
- private void waitForFuturesRespectingTimeout(final List<HealthCheckFuture> futuresForResultOfThisCall,
- HealthCheckExecutionOptions options) {
-
- final long callExcutionStartTime = System.currentTimeMillis();
- boolean allFuturesDone;
-
- long effectiveTimeout = this.timeoutInMs;
- if (options != null && options.getOverrideGlobalTimeout() > 0) {
- effectiveTimeout = options.getOverrideGlobalTimeout();
- }
-
- if (futuresForResultOfThisCall.isEmpty()) {
- return; // nothing to wait for (usually because of cached results)
- }
-
- do {
- try {
- synchronized (stillRunningFutures) {
- stillRunningFutures.wait(50); // wait for notifications of callbacks of HealthCheckFutures
- }
- } catch (final InterruptedException ie) {
- logger.warn("Unexpected InterruptedException while waiting for healthCheckContributors", ie);
- }
-
- allFuturesDone = true;
- for (final HealthCheckFuture healthCheckFuture : futuresForResultOfThisCall) {
- allFuturesDone &= healthCheckFuture.isDone();
- }
- } while (!allFuturesDone && (System.currentTimeMillis() - callExcutionStartTime) < effectiveTimeout);
- }
-
- /** Collect the results from all futures
- *
- * @param futuresForResultOfThisCall The list of futures
- * @param results The result collection */
- void collectResultsFromFutures(final List<HealthCheckFuture> futuresForResultOfThisCall,
- final Collection<HealthCheckExecutionResult> results) {
-
- final Set<HealthCheckExecutionResult> resultsFromFutures = new HashSet<HealthCheckExecutionResult>();
-
- final Iterator<HealthCheckFuture> futuresIt = futuresForResultOfThisCall.iterator();
- while (futuresIt.hasNext()) {
- final HealthCheckFuture future = futuresIt.next();
- final HealthCheckExecutionResult result = this.collectResultFromFuture(future);
-
- resultsFromFutures.add(result);
- futuresIt.remove();
- }
-
- logger.debug("Adding {} results from futures", resultsFromFutures.size());
- results.addAll(resultsFromFutures);
- }
-
- /** Collect the result from a single future
- *
- * @param future The future
- * @return The execution result or a result for a reached timeout */
- HealthCheckExecutionResult collectResultFromFuture(final HealthCheckFuture future) {
-
- HealthCheckExecutionResult result;
- HealthCheckMetadata hcMetadata = future.getHealthCheckMetadata();
- if (future.isDone()) {
- logger.debug("Health Check is done: {}", hcMetadata);
-
- try {
- result = future.get();
- } catch (final Exception e) {
- logger.warn("Unexpected Exception during future.get(): " + e, e);
- long futureElapsedTimeMs = new Date().getTime() - future.getCreatedTime().getTime();
- result = new ExecutionResult(hcMetadata, Result.Status.HEALTH_CHECK_ERROR,
- "Unexpected Exception during future.get(): " + e, futureElapsedTimeMs, false);
- }
-
- } else {
- logger.debug("Health Check timed out: {}", hcMetadata);
- // Futures must not be cancelled as interrupting a health check might leave the system in invalid state
- // (worst case could be a corrupted repository index if using write operations)
-
- // normally we turn the check into WARN (normal timeout), but if the threshold time for CRITICAL is reached for a certain
- // future we turn the result CRITICAL
- long futureElapsedTimeMs = new Date().getTime() - future.getCreatedTime().getTime();
- FormattingResultLog resultLog = new FormattingResultLog();
- if (futureElapsedTimeMs < this.longRunningFutureThresholdForRedMs) {
- resultLog.warn("Timeout: Check still running after " + msHumanReadable(futureElapsedTimeMs));
- } else {
- resultLog.critical("Timeout: Check still running after " + msHumanReadable(futureElapsedTimeMs)
- + " (exceeding the configured threshold for CRITICAL: "
- + msHumanReadable(this.longRunningFutureThresholdForRedMs) + ")");
- }
-
- // add logs from previous, cached result if exists (using a 1 year TTL)
- HealthCheckExecutionResult lastCachedResult = healthCheckResultCache.getValidCacheResult(hcMetadata, 1000 * 60 * 60 * 24 * 365);
- if (lastCachedResult != null) {
- DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");
- resultLog.info("*** Result log of last execution finished at {} after {} ***",
- df.format(lastCachedResult.getFinishedAt()),
- msHumanReadable(lastCachedResult.getElapsedTimeInMs()));
- for (ResultLog.Entry entry : lastCachedResult.getHealthCheckResult()) {
- resultLog.add(entry);
- }
- }
-
- result = new ExecutionResult(hcMetadata, new Result(resultLog), futureElapsedTimeMs, true);
-
- }
-
- return result;
- }
-
- public void setTimeoutInMs(final long timeoutInMs) {
- this.timeoutInMs = timeoutInMs;
- }
-
- public void setLongRunningFutureThresholdForRedMs(
- final long longRunningFutureThresholdForRedMs) {
- this.longRunningFutureThresholdForRedMs = longRunningFutureThresholdForRedMs;
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImplConfiguration.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImplConfiguration.java
deleted file mode 100644
index 7fe870163e..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImplConfiguration.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@ObjectClassDefinition(name = "Apache Felix Health Check Executor", description = "Runs health checks for a given list of tags in parallel.")
-@interface HealthCheckExecutorImplConfiguration {
-
- long TIMEOUT_DEFAULT_MS = 2000L;
-
- long LONGRUNNING_FUTURE_THRESHOLD_CRITICAL_DEFAULT_MS = 1000L * 60 * 5; // 5 min default
-
- long RESULT_CACHE_TTL_DEFAULT_MS = 1000L * 2;
-
- long TEMPORARILY_UNAVAILABLE_GRACE_PERIOD_DEFAULT_MS = 1000L * 60 * 10; // 10 min default
-
- @AttributeDefinition(name = "Timeout", description = "Timeout in ms until a check is marked as timed out")
- long timeoutInMs() default TIMEOUT_DEFAULT_MS;
-
- @AttributeDefinition(name = "Timeout threshold for CRITICAL", description = "Threshold in ms until a check is marked as 'exceedingly' timed out and will marked CRITICAL instead of WARN only")
- long longRunningFutureThresholdForCriticalMs() default LONGRUNNING_FUTURE_THRESHOLD_CRITICAL_DEFAULT_MS;
-
- @AttributeDefinition(name = "Results Cache TTL in Ms", description = "Result Cache time to live - results will be cached for the given time")
- long resultCacheTtlInMs() default RESULT_CACHE_TTL_DEFAULT_MS;
-
- @AttributeDefinition(name = "TEMPORARILY_UNAVAILABLE Grace Period", description = "Grace period in ms until a continuously reported TEMPORARILY_UNAVAILABLE check becomes CRITICAL")
- long temporarilyAvailableGracePeriodInMs() default TEMPORARILY_UNAVAILABLE_GRACE_PERIOD_DEFAULT_MS;
-
- @AttributeDefinition(name = "Default Tags", description = "Default tags to be executed if no tags are explicitly supplied")
- String[] defaultTags() default {"default"};
-
- @AttributeDefinition(name = "Auto Logging", description = "If enabled, will automatically log entries of ResultLog (or FormattingResultLog resp.) using Log4j. The logging category used is the class instantiating ResultLog prefixed with 'healthchecks.', for instance 'healthchecks.com.mycorp.myplatform.mymodule.ModuleCheck'.")
- boolean autoLogging() default false;
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPool.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPool.java
deleted file mode 100644
index aa4ddb7203..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPool.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import java.util.concurrent.RejectedExecutionHandler;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.metatype.annotations.Designate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Creates a thread pool via standard java.util.concurrent package to be used for parallel execution of health checks in
- * HealthCheckExecutorImpl and AsyncHealthCheckExecutor */
-@Component(service = { HealthCheckExecutorThreadPool.class })
-@Designate(ocd = HealthCheckExecutorThreadPoolConfiguration.class)
-public class HealthCheckExecutorThreadPool {
- private final static Logger LOG = LoggerFactory.getLogger(HealthCheckExecutorThreadPool.class);
-
- private int threadPoolSize;
-
- private ScheduledThreadPoolExecutor executor;
-
- @Activate
- protected final void activate(final HealthCheckExecutorThreadPoolConfiguration configuration, final BundleContext bundleContext) {
-
- this.threadPoolSize = configuration.threadPoolSize();
-
- executor = new ScheduledThreadPoolExecutor(threadPoolSize, new HcThreadFactory(), new HcRejectedExecutionHandler());
-
- LOG.info("Created HC Thread Pool: threadPoolSize={}", threadPoolSize);
-
- }
-
- @Deactivate
- protected final void deactivate() {
- executor.shutdown();
- }
-
- // Method called by HealthCheckExecutorImpl (regular synchronous checks)
- public void execute(final Runnable job) {
- this.executor.execute(job);
- }
-
- // used for interval execution (asynchronous checks)
- public ScheduledFuture<?> scheduleAtFixedRate(final Runnable job, long intervalInSec) {
- ScheduledFuture<?> scheduleFuture = executor.scheduleAtFixedRate(job, 0, intervalInSec, TimeUnit.SECONDS);
- return scheduleFuture;
- }
-
- // methods below are used by AsyncHealthCheckExecutor.QuartzThreadPool
- public int getPoolSize() {
- return this.executor.getPoolSize();
- }
-
- public int getMaxCurrentlyAvailableThreads() {
- return this.threadPoolSize - executor.getQueue().size();
- }
-
- static class HcThreadFactory implements ThreadFactory {
- private final ThreadGroup group;
- private final AtomicInteger threadNumber = new AtomicInteger(1);
-
- HcThreadFactory() {
- group = Thread.currentThread().getThreadGroup();
- }
-
- public Thread newThread(Runnable r) {
- Thread t = new Thread(group, r, "hc-thread-" + threadNumber.getAndIncrement());
- t.setDaemon(true); // using daemon thread to not delay JVM shutdown (HC status is non-transactional and only in memory)
- t.setPriority(Thread.NORM_PRIORITY);
- return t;
- }
- }
-
- private final class HcRejectedExecutionHandler implements RejectedExecutionHandler {
- @Override
- public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
- LOG.warn("Thread Pool {} rejected to run runnable {}", executor, r);
- }
- }
-
- public ScheduledExecutorService getExecutor() {
- return executor;
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPoolConfiguration.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPoolConfiguration.java
deleted file mode 100644
index f9e2144b02..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorThreadPoolConfiguration.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@ObjectClassDefinition(name = "Apache Felix Health Check Executor Thread Pool", description = "Runs health checks for a given list of tags in parallel.")
-@interface HealthCheckExecutorThreadPoolConfiguration {
-
- int THREAD_POOL_SIZE_DEFAULT = 25;
-
- @AttributeDefinition(name = "Thread Pool Size", description = "Number of threads to be used for parallel health check execution")
- int threadPoolSize() default THREAD_POOL_SIZE_DEFAULT;
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckFuture.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckFuture.java
deleted file mode 100644
index 93e3cbed7d..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckFuture.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
-
-import org.apache.felix.hc.api.FormattingResultLog;
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Future to be able to schedule a health check for parallel execution. */
-public class HealthCheckFuture extends FutureTask<ExecutionResult> {
-
- public interface Callback {
- public void finished(final HealthCheckExecutionResult result);
- }
-
- private final static Logger LOG = LoggerFactory.getLogger(HealthCheckFuture.class);
-
- private static Result createResult(final HealthCheckMetadata metadata, Exception e) {
- return new Result(Result.Status.HEALTH_CHECK_ERROR, "Exception during execution of '" + metadata.getName() + "'", e);
- }
-
- private final HealthCheckMetadata metadata;
- private final Date createdTime;
-
- public HealthCheckFuture(final HealthCheckMetadata metadata, final BundleContext bundleContext, final Callback callback) {
- super(new Callable<ExecutionResult>() {
- @Override
- public ExecutionResult call() throws Exception {
- Thread.currentThread().setName("HealthCheck " + metadata.getTitle());
- LOG.debug("Starting check {}", metadata);
-
- long startTime = System.currentTimeMillis();
- Result resultFromHealthCheck = null;
- ExecutionResult executionResult = null;
-
- Object healthCheckObject = bundleContext.getService(metadata.getServiceReference());
- try {
- if (healthCheckObject != null) {
- HealthCheck healthCheck = healthCheckObject instanceof HealthCheck
- ? (HealthCheck) healthCheckObject
- : new LegacyHealthCheckWrapper(healthCheckObject);
- resultFromHealthCheck = healthCheck.execute();
-
- } else {
- throw new IllegalStateException("Service cannot be retrieved - probably activate() failed or there are unsatisfied references");
- }
- } catch (final Exception e) {
- resultFromHealthCheck = createResult(metadata, e);
- } catch (final Throwable t) {
- Exception e = new RuntimeException("System error during health check execution", t);
- resultFromHealthCheck = createResult(metadata, e);
- } finally {
- // unget service ref
- bundleContext.ungetService(metadata.getServiceReference());
-
- // update result with information about this run
- long elapsedTime = (System.currentTimeMillis() - startTime);
- if (resultFromHealthCheck != null) {
- // wrap the result in an execution result
- executionResult = new ExecutionResult(metadata, resultFromHealthCheck, elapsedTime);
- }
- LOG.debug("Time consumed for {}: {}", metadata, msHumanReadable(elapsedTime));
- }
-
- callback.finished(executionResult);
- Thread.currentThread().setName("HealthCheck-idle");
- return executionResult;
- }
-
- });
- this.createdTime = new Date();
- this.metadata = metadata;
-
- }
-
- Date getCreatedTime() {
- return this.createdTime;
- }
-
- public HealthCheckMetadata getHealthCheckMetadata() {
- return metadata;
- }
-
- @Override
- public String toString() {
- return "[Future for " + this.metadata + ", createdTime=" + this.createdTime + "]";
- }
-
- private static class LegacyHealthCheckWrapper implements HealthCheck {
- private final Object legacyHealthCheck;
- private final FormattingResultLog log;
-
- public LegacyHealthCheckWrapper(Object legacyHealthCheck) {
- this.legacyHealthCheck = legacyHealthCheck;
- this.log = new FormattingResultLog();
- }
-
- @Override
- @SuppressWarnings("rawtypes")
- public Result execute() {
- Class<?> hcClass = legacyHealthCheck.getClass();
- log.debug("Running legacy HC {}, please convert to new interface org.apache.felix.hc.api.HealthCheck!",
- hcClass.getName());
-
- Object result;
- try {
- Method executeMethod = hcClass.getMethod("execute");
- result = executeMethod.invoke(legacyHealthCheck);
- } catch (InvocationTargetException e) {
- log.healthCheckError("Exception during execute() of Sling HC {}: {}", hcClass.getName(), String.valueOf(e.getTargetException()), e);
- return new Result(log);
- } catch (ReflectiveOperationException e) {
- log.healthCheckError("Could not call Sling HC {} from Felix Runtime: {}", hcClass.getName(), String.valueOf(e), e);
- return new Result(log);
- }
-
- try {
- Object resultLog = readPrivateField(result, "resultLog");
-
- List<?> entries = (List) readPrivateField(resultLog, "entries");
- if(entries != null) {
- for (Object object : entries) {
- String statusLegacy = String.valueOf(readPrivateField(object, "status"));
- String message = (String) readPrivateField(object, "message");
- Exception exception = (Exception) readPrivateField(object, "exception");
- if(statusLegacy.equals("DEBUG")) {
- log.add(new ResultLog.Entry(message, true, exception));
- } else {
- statusLegacy = statusLegacy.replace("INFO", Result.Status.OK.name());
- log.add(new ResultLog.Entry(Result.Status.valueOf(statusLegacy), message, exception));
- }
- }
- }
- } catch (ReflectiveOperationException e) {
- log.healthCheckError("Could convert Sling HC result of {} for Felix Runtime: {}", hcClass.getName(), String.valueOf(e), e);
- }
- return new Result(log);
- }
-
- private Object readPrivateField(Object target, String fieldName) throws ReflectiveOperationException {
- Field field = target.getClass().getDeclaredField(fieldName);
- field.setAccessible(true);
- return field.get(target);
- }
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckResultCache.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckResultCache.java
deleted file mode 100644
index c021b82be2..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/HealthCheckResultCache.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.Result.Status;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.ResultLog.Entry;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Caches health check results. */
-public class HealthCheckResultCache {
-
- private final Logger logger = LoggerFactory.getLogger(this.getClass());
-
- /** General cache. */
- private final Map<Long, HealthCheckExecutionResult> cache = new ConcurrentHashMap<Long, HealthCheckExecutionResult>();
-
- /** Cache for sticky results */
- private final Map<Result.Status, Map<Long, HealthCheckExecutionResult>> cacheOfNotOkResults = new ConcurrentHashMap<Result.Status, Map<Long, HealthCheckExecutionResult>>();
-
- /** Update the cache with the result */
- public void updateWith(HealthCheckExecutionResult result) {
- final ExecutionResult executionResult = (ExecutionResult) result;
- final HealthCheckExecutionResult previous = cache.put(executionResult.getServiceId(), result);
- if ( previous == null || previous.getHealthCheckResult().getStatus() != result.getHealthCheckResult().getStatus()) {
- logger.debug("Updated HC result for {} : {}", result.getHealthCheckMetadata().getName(), result.getHealthCheckResult());
- }
-
- // update cache for sticky handling
- Status status = executionResult.getHealthCheckResult().getStatus();
- if (status != Result.Status.OK) {
- logger.debug("Caching {} result for HC {}", status, executionResult.getServiceId());
- Map<Long, HealthCheckExecutionResult> nonOkResultsForStatus = cacheOfNotOkResults.get(status);
- if(nonOkResultsForStatus==null) {
- nonOkResultsForStatus = new ConcurrentHashMap<Long, HealthCheckExecutionResult>();
- cacheOfNotOkResults.put(status, nonOkResultsForStatus);
- }
- nonOkResultsForStatus.put(executionResult.getServiceId(), result);
- }
- }
-
- /**
- * Get the valid cache results for given metadata list
- *
- * @param metadatas list of metadata to get results for. For each found result the metadata will be deleted
- * @param results matching cached results are added to this list
- */
- public void useValidCacheResults(final List<HealthCheckMetadata> metadatas,
- final Collection<HealthCheckExecutionResult> results,
- final long resultCacheTtlInMs) {
- final Set<HealthCheckExecutionResult> cachedResults = new TreeSet<HealthCheckExecutionResult>();
- final Iterator<HealthCheckMetadata> checksIt = metadatas.iterator();
- while (checksIt.hasNext()) {
- final HealthCheckMetadata md = checksIt.next();
- final HealthCheckExecutionResult result = getValidCacheResult(md, resultCacheTtlInMs);
- if (result != null) {
- cachedResults.add(result);
- checksIt.remove();
- }
- }
- logger.debug("Adding {} results from cache", cachedResults.size());
- results.addAll(cachedResults);
- }
-
- /** Return the cached result if it's still valid. */
- public HealthCheckExecutionResult getValidCacheResult(final HealthCheckMetadata metadata,
- final long resultCacheTtlInMs) {
- return get(metadata, resultCacheTtlInMs);
- }
-
- private HealthCheckExecutionResult get(final HealthCheckMetadata metadata, final long globalResultCacheTtlInMs) {
- final Long key = metadata.getServiceId();
- final HealthCheckExecutionResult cachedResult = cache.get(key);
- if (cachedResult != null) {
- Date finishedAt = cachedResult.getFinishedAt();
- if (finishedAt == null) {
- // never cache without proper meta data -> remove it
- cache.remove(key);
- return null;
- }
-
- long effectiveTtl = getEffectiveTtl(metadata, globalResultCacheTtlInMs);
- long validUntilLong = finishedAt.getTime() + effectiveTtl;
- if (validUntilLong < 0) { // if Long.MAX_VALUE is configured, this can become negative
- validUntilLong = Long.MAX_VALUE;
- }
- Date validUntil = new Date(validUntilLong);
- Date now = new Date();
- if (validUntil.after(now)) {
- logger.debug("Cache hit: validUntil={} cachedResult={}", validUntil, cachedResult);
- return cachedResult;
- } else {
- logger.debug("Outdated result: validUntil={} cachedResult={}", validUntil, cachedResult);
- // not removing result for key as out-dated results are shown for timed out checks if available
- }
- }
-
- // null => no cache hit
- return null;
- }
-
- /** Obtains the effective TTL for a given Metadata descriptor.
- *
- * @param metadata Metadata descriptor of health check
- * @param globalTtl TTL from service configuration of health check executor (used as default)
- * @return effective TTL */
- private long getEffectiveTtl(HealthCheckMetadata metadata, long globalTtl) {
- final long ttl;
- Long hcTtl = metadata.getResultCacheTtlInMs();
- if (hcTtl != null && hcTtl > -1) {
- ttl = hcTtl;
- } else {
- ttl = globalTtl;
- }
- return ttl;
- }
-
- /** Creates a new execution result
- *
- * @param origResult
- * @return */
- public HealthCheckExecutionResult createExecutionResultWithStickyResults(HealthCheckExecutionResult origResult) {
- HealthCheckExecutionResult result = origResult;
-
- HealthCheckMetadata healthCheckMetadata = origResult.getHealthCheckMetadata();
- Long keepNonOkResultsStickyForSec = healthCheckMetadata.getKeepNonOkResultsStickyForSec();
- if (keepNonOkResultsStickyForSec != null && keepNonOkResultsStickyForSec > 0) {
- logger.debug("Taking into account sticky results (up to {} sec old) for health check {}", keepNonOkResultsStickyForSec,
- healthCheckMetadata.getName());
- List<HealthCheckExecutionResult> nonOkResultsFromPast = new ArrayList<HealthCheckExecutionResult>();
- long cutOffTime = System.currentTimeMillis() - (keepNonOkResultsStickyForSec * 1000);
- for (Status status : cacheOfNotOkResults.keySet()) {
- long hcServiceId = ((ExecutionResult) origResult).getServiceId();
- HealthCheckExecutionResult nonOkResultFromPast = cacheOfNotOkResults.get(status).get(hcServiceId);
- if (nonOkResultFromPast == null) {
- logger.debug("no sticky result in cache for HC {}", hcServiceId);
- continue;
- }
- if (nonOkResultFromPast == origResult) {
- logger.debug("result already in cache: {} for HC {}, not adding sticky result", origResult, hcServiceId);
- continue;
- }
- long pastHcTime = nonOkResultFromPast.getFinishedAt().getTime();
- logger.debug("Time of old {} result: {}", status, pastHcTime);
- logger.debug("Cut off time: {}", cutOffTime);
- if (pastHcTime > cutOffTime) {
- logger.debug("Found sticky result: {}", nonOkResultFromPast);
- nonOkResultsFromPast.add(nonOkResultFromPast);
- }
- }
-
- if (!nonOkResultsFromPast.isEmpty()) {
- ResultLog resultLog = new ResultLog();
- resultLog.add(new Entry(Result.Status.OK, "*** Current Result: "+origResult.getHealthCheckResult().getStatus()+" ***"));
- for (ResultLog.Entry entry : origResult.getHealthCheckResult()) {
- resultLog.add(entry);
- }
- DateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");
- for (HealthCheckExecutionResult nonOkResultFromPast : nonOkResultsFromPast) {
- Status status = nonOkResultFromPast.getHealthCheckResult().getStatus();
- resultLog.add(
- new Entry(Result.Status.OK,
- "*** Sticky Result: " + status + " from " + df.format(nonOkResultFromPast.getFinishedAt()) + " ***"));
- for (ResultLog.Entry entry : nonOkResultFromPast.getHealthCheckResult()) {
- resultLog.add(entry);
- }
- }
- result = new ExecutionResult(healthCheckMetadata, new Result(resultLog), origResult.getFinishedAt(), origResult.getElapsedTimeInMs(), false);
- }
- }
-
- return result;
- }
-
- /** Clear the whole cache */
- public void clear() {
- this.cache.clear();
- this.cacheOfNotOkResults.clear();
- }
-
- /** Remove entry from cache */
- public void removeCachedResult(final Long serviceId) {
- this.cache.remove(serviceId);
- for (Map<Long, HealthCheckExecutionResult> cacheOfNotOkResultsForStatus : cacheOfNotOkResults.values()) {
- cacheOfNotOkResultsForStatus.remove(serviceId);
- }
- }
-
- @Override
- public String toString() {
- return "[HealthCheckResultCache size=" + cache.size() + "]";
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/TempUnavailableGracePeriodEvaluator.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/TempUnavailableGracePeriodEvaluator.java
deleted file mode 100644
index ab3c66de8a..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/TempUnavailableGracePeriodEvaluator.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
-
-import java.util.Date;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.ResultLog.Entry;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Checks a result set for TEMPORARILY_UNAVAILABLE that have exceeded grace period. */
-public class TempUnavailableGracePeriodEvaluator {
-
- private static final Logger LOG = LoggerFactory.getLogger(TempUnavailableGracePeriodEvaluator.class);
-
- /** Timestamp for last TEMPORARILY_UNAVAILABLE result */
- private final Map<Long, Date> firstTempUnavailableDateByServiceId = new ConcurrentHashMap<Long, Date>();
-
- private final long temporarilyAvailableGracePeriodInMs;
-
- public TempUnavailableGracePeriodEvaluator(long temporarilyAvailableGracePeriodInMs) {
- this.temporarilyAvailableGracePeriodInMs = temporarilyAvailableGracePeriodInMs;
- LOG.debug("Configured temporarilyAvailableGracePeriodInMs: {}", temporarilyAvailableGracePeriodInMs);
- }
-
- void updateTemporarilyUnavailableTimestampWith(HealthCheckExecutionResult result) {
- final ExecutionResult executionResult = (ExecutionResult) result;
- if (executionResult.getHealthCheckResult().getStatus() == Result.Status.TEMPORARILY_UNAVAILABLE) {
- if (!firstTempUnavailableDateByServiceId.containsKey(executionResult.getServiceId())) {
- firstTempUnavailableDateByServiceId.put(executionResult.getServiceId(), executionResult.getFinishedAt());
- } // else keep date of first occurrence of TEMPORARILY_UNAVAILABLE
- } else {
- firstTempUnavailableDateByServiceId.remove(executionResult.getServiceId());
- }
- }
-
- void evaluateGracePeriodForTemporarilyUnavailableResults(List<HealthCheckExecutionResult> results) {
- ListIterator<HealthCheckExecutionResult> resultsIt = results.listIterator();
- while (resultsIt.hasNext()) {
- ExecutionResult executionResult = (ExecutionResult) resultsIt.next();
- if (executionResult.getHealthCheckResult().getStatus() != Result.Status.TEMPORARILY_UNAVAILABLE) {
- continue; // not TEMPORARILY_UNAVAILABLE
- }
-
- Date firstTempUnavailableDate = firstTempUnavailableDateByServiceId.get(executionResult.getServiceId());
-
- if(firstTempUnavailableDate == null) {
- continue; // no previous TEMPORARILY_UNAVAILABLE found
- }
-
- long timestampForCritical = firstTempUnavailableDate.getTime() + temporarilyAvailableGracePeriodInMs;
- if (executionResult.getFinishedAt().getTime() < timestampForCritical) {
- continue; // grace period not exceeded
- }
-
- ResultLog resultLog = new ResultLog();
- for (ResultLog.Entry entry : executionResult.getHealthCheckResult()) {
- resultLog.add(entry);
- }
-
- resultLog.add(new Entry(Result.Status.CRITICAL, "Grace period for being temporarily unavailable exceeded "
- + "by " + msHumanReadable(executionResult.getFinishedAt().getTime() - timestampForCritical)
- + " (configured grace period: " + msHumanReadable(temporarilyAvailableGracePeriodInMs) + ")"));
- executionResult = new ExecutionResult(executionResult.getHealthCheckMetadata(), new Result(resultLog),
- executionResult.getFinishedAt(), executionResult.getElapsedTimeInMs(), false);
- resultsIt.set(executionResult);
-
- }
- }
-
-
- @Override
- public String toString() {
- return "[TempUnavailableGracePeriodTester count checks currently TEMPORARILY_UNAVAILABLE: " + firstTempUnavailableDateByServiceId.size() + "]";
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/async/AsyncHealthCheckExecutor.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/async/AsyncHealthCheckExecutor.java
deleted file mode 100644
index 97df0f6cda..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/executor/async/AsyncHealthCheckExecutor.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor.async;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.executor.ExecutionResult;
-import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.apache.felix.hc.core.impl.executor.HealthCheckFuture;
-import org.apache.felix.hc.core.impl.executor.HealthCheckFuture.Callback;
-import org.apache.felix.hc.core.impl.executor.HealthCheckResultCache;
-import org.apache.felix.hc.core.impl.scheduling.AsyncIntervalJob;
-import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.apache.felix.hc.core.impl.scheduling.CronJobFactory;
-import org.apache.felix.hc.core.impl.util.HealthCheckFilter;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Runs health checks asynchronously, either via cron or via interval. */
-@Component(service = AsyncHealthCheckExecutor.class, immediate = true)
-public class AsyncHealthCheckExecutor implements ServiceListener {
-
- private static final Logger LOG = LoggerFactory.getLogger(AsyncHealthCheckExecutor.class);
-
- private Map<HealthCheckMetadata, ExecutionResult> asyncResultsByDescriptor = new ConcurrentHashMap<>();
-
- private Map<HealthCheckMetadata, AsyncJob> registeredJobs = new HashMap<>();
-
- private BundleContext bundleContext;
-
- @Reference
- HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
-
- @Reference
- CronJobFactory cronJobFactory;
-
- @Activate
- protected final void activate(final BundleContext bundleContext) throws InvalidSyntaxException {
- this.bundleContext = bundleContext;
- this.bundleContext.addServiceListener(this, "(objectclass=" + HealthCheck.class.getName() + ")");
- int count = 0;
- HealthCheckFilter healthCheckFilter = new HealthCheckFilter(bundleContext);
- final ServiceReference<?>[] healthCheckReferences = healthCheckFilter.getHealthCheckServiceReferences(HealthCheckSelector.empty(), false);
- for (ServiceReference<?> serviceReference : healthCheckReferences) {
- HealthCheckMetadata healthCheckMetadata = new HealthCheckMetadata(serviceReference);
- if (isAsync(healthCheckMetadata)) {
- if (scheduleHealthCheck(healthCheckMetadata)) {
- count++;
- }
- }
- }
- LOG.debug("Scheduled {} jobs for asynchronous health checks during bundle startup", count);
- }
-
- @Deactivate
- protected final void deactivate() {
- this.bundleContext.removeServiceListener(this);
- this.bundleContext = null;
-
- LOG.debug("Unscheduling {} jobs for asynchronous health checks", registeredJobs.size());
- for (HealthCheckMetadata healthCheckDescriptor : new LinkedList<HealthCheckMetadata>(registeredJobs.keySet())) {
- unscheduleHealthCheck(healthCheckDescriptor);
- }
- }
-
- @Override
- public void serviceChanged(ServiceEvent event) {
- if (bundleContext == null) {
- // already deactivated?
- return;
- }
- ServiceReference<?> serviceReference = event.getServiceReference();
- final boolean isHealthCheck = serviceReference.isAssignableTo(bundleContext.getBundle(), HealthCheck.class.getName());
-
- if (isHealthCheck) {
- HealthCheckMetadata healthCheckMetadata = new HealthCheckMetadata(serviceReference);
- int eventType = event.getType();
- if (eventType == ServiceEvent.REGISTERED) {
- LOG.debug("Received service event REGISTERED for health check {}", healthCheckMetadata);
- scheduleHealthCheck(healthCheckMetadata);
- } else if (eventType == ServiceEvent.UNREGISTERING) {
- LOG.debug("Received service event UNREGISTERING for health check {}", healthCheckMetadata);
- unscheduleHealthCheck(healthCheckMetadata);
- } else if (eventType == ServiceEvent.MODIFIED) {
- LOG.debug("Received service event MODIFIED for health check {}", healthCheckMetadata);
- unscheduleHealthCheck(healthCheckMetadata);
- scheduleHealthCheck(healthCheckMetadata);
- }
-
- }
- }
-
- private boolean scheduleHealthCheck(HealthCheckMetadata descriptor) {
- try {
- AsyncJob healthCheckAsyncJob = null;
- if (isAsyncCron(descriptor)) {
- healthCheckAsyncJob = cronJobFactory.createAsyncCronJob(getAsyncJob(descriptor), "job-hc-" + descriptor.getTitle().replaceAll("\\s+","-"), "async-healthchecks", descriptor.getAsyncCronExpression());
- } else if (isAsyncInterval(descriptor)) {
- healthCheckAsyncJob = new AsyncIntervalJob(getAsyncJob(descriptor), healthCheckExecutorThreadPool, descriptor.getAsyncIntervalInSec());
- }
- if (healthCheckAsyncJob != null) {
- healthCheckAsyncJob.schedule();
- registeredJobs.put(descriptor, healthCheckAsyncJob);
- return true;
- } else {
- return false;
- }
-
- } catch (Exception e) {
- LOG.warn("Could not schedule async health check for " + descriptor + ". Exception: " + e, e);
- return false;
- }
- }
-
- private Runnable getAsyncJob(HealthCheckMetadata descriptor) {
-
- return new Runnable() {
- @Override
- public void run() {
- LOG.debug("Running job {}", this);
- HealthCheckFuture healthCheckFuture = new HealthCheckFuture(descriptor, bundleContext, new Callback() {
-
- @Override
- public void finished(HealthCheckExecutionResult result) {
- updateWith(result);
- }
- });
-
- // run future in same thread (as we are already async via scheduler)
- healthCheckFuture.run();
- }
-
- @Override
- public String toString() {
- return descriptor.toString();
- }
- };
- }
-
- private boolean unscheduleHealthCheck(HealthCheckMetadata descriptor) {
-
- // here no check for isAsync must be used to ensure previously
- // scheduled async checks are correctly unscheduled if they have
- // changed from async to sync.
-
- AsyncJob job = registeredJobs.remove(descriptor);
- if (job != null) {
- return job.unschedule();
- } else {
- LOG.debug("No job was unregistered for descriptor {}", descriptor);
- return false;
- }
- }
-
- /** Called by the main Executor to get results from async HCs */
- public void collectAsyncResults(List<HealthCheckMetadata> healthCheckDescriptors, Collection<HealthCheckExecutionResult> results,
- HealthCheckResultCache cache) {
- Iterator<HealthCheckMetadata> checksIt = healthCheckDescriptors.iterator();
-
- Set<ExecutionResult> asyncResults = new TreeSet<ExecutionResult>();
- while (checksIt.hasNext()) {
- HealthCheckMetadata healthCheckMetadata = checksIt.next();
- if (isAsync(healthCheckMetadata)) {
- ExecutionResult result = asyncResultsByDescriptor.get(healthCheckMetadata);
- if (result == null) {
- result = handleMissingResult(healthCheckMetadata);
- }
- asyncResults.add(result);
- // remove from HC collection to not execute the check in HealthCheckExecutorImpl
- checksIt.remove();
- }
- }
-
- LOG.debug("Caching {} results from async results", asyncResults.size());
- for (ExecutionResult result : asyncResults) {
- cache.updateWith(result);
- }
-
- LOG.debug("Adding {} results from async results", asyncResults.size());
- results.addAll(asyncResults);
-
- }
-
- private ExecutionResult handleMissingResult(HealthCheckMetadata healthCheckMetadata) {
- ExecutionResult result;
- if(isAsyncCron(healthCheckMetadata)) {
- if(registeredJobs.containsKey(healthCheckMetadata)) {
- result = new ExecutionResult(healthCheckMetadata,
- new Result(Result.Status.OK, "Async Health Check with cron expression '" + healthCheckMetadata.getAsyncCronExpression() +
- "' has not yet been executed."), 0L);
- } else {
- result = new ExecutionResult(healthCheckMetadata,
- new Result(Result.Status.WARN, "Async Health Check with cron expression '" + healthCheckMetadata.getAsyncCronExpression() +
- "' is never executed because of misconfiguration."), 0L);
- }
-
- } else {
- result = new ExecutionResult(healthCheckMetadata,
- new Result(Result.Status.OK, "Async Health Check with interval '" + healthCheckMetadata.getAsyncIntervalInSec() +
- "' has not yet been executed."), 0L);
- }
- return result;
- }
-
- public void updateWith(HealthCheckExecutionResult result) {
- if (isAsync(result.getHealthCheckMetadata())) {
- asyncResultsByDescriptor.put(result.getHealthCheckMetadata(), (ExecutionResult) result);
- LOG.debug("Updated result for async hc {} with {}", result.getHealthCheckMetadata(), result);
- }
- }
-
- private boolean isAsync(HealthCheckMetadata healthCheckMetadata) {
- return isAsyncCron(healthCheckMetadata) || isAsyncInterval(healthCheckMetadata);
- }
-
- private boolean isAsyncCron(HealthCheckMetadata healthCheckMetadata) {
- return StringUtils.isNotBlank(healthCheckMetadata.getAsyncCronExpression());
- }
-
- private boolean isAsyncInterval(HealthCheckMetadata healthCheckMetadata) {
- return healthCheckMetadata.getAsyncIntervalInSec() != null && healthCheckMetadata.getAsyncIntervalInSec() > 0L;
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/filter/AdhocResultDuringRequestProcessingFilter.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/filter/AdhocResultDuringRequestProcessingFilter.java
deleted file mode 100644
index 11936f5e11..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/filter/AdhocResultDuringRequestProcessingFilter.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.filter;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import jakarta.servlet.Filter;
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.FilterConfig;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.ServletRequest;
-import jakarta.servlet.ServletResponse;
-import jakarta.servlet.http.HttpServletRequest;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-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.servlet.ResultTxtVerboseSerializer;
-import org.apache.felix.hc.core.impl.util.AdhocStatusHealthCheck;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.component.ComponentContext;
-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.Reference;
-import org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Dynamically adds a HC result for configured requests. */
-@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
-@Designate(ocd = AdhocResultDuringRequestProcessingFilter.Config.class, factory = true)
-public class AdhocResultDuringRequestProcessingFilter implements Filter {
- private static final Logger LOG = LoggerFactory.getLogger(AdhocResultDuringRequestProcessingFilter.class);
-
-
- @ObjectClassDefinition(name = "Health Check Adhoc Result during Request Processing", description = "Registers an health check with an adhoc result during request processing")
- public @interface Config {
-
- @AttributeDefinition(name = "Filter Request Path RegEx", description = "Regex to be matched against request path")
- String osgi_http_whiteboard_filter_regex();
-
- @AttributeDefinition(name = "Filter Context", description = "Needs to be set to correct whiteboard context filter (e.g. '(osgi.http.whiteboard.context.name=default)'")
- String osgi_http_whiteboard_context_select() default "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=*)";
-
- @AttributeDefinition(name = "Request Method", description = "Relevant request method (leave empty to not restrict to a method)")
- String method() default "";
-
- @AttributeDefinition(name = "User Agent RegEx", description = "Relevant user agent header (leave emtpy to not restrict to a user agent)")
- String userAgentRegEx() default "";
-
- @AttributeDefinition(name = "Health Check Name", description = "Name of health check during request processing")
- String hcName() default "Ongoing request";
-
- @AttributeDefinition(name = "Tags to register", description = "List of tags the adhoc result shall be registered for (tags are not active during configured delay in case 'delayProcessingInSec' is configured)")
- String[] tags() default {};
-
- @AttributeDefinition(name = "Status during request processing", description = "Status to be sent during request processing")
- Result.Status statusDuringRequestProcessing() default Result.Status.TEMPORARILY_UNAVAILABLE;
-
- @AttributeDefinition(name = "Delay before request processing", description = "Time to delay processing of request in sec (the default 0 turns the delay off). Use together with 'tagsDuringDelayedProcessing' advertise request processing before actual action (e.g. to signal a deployment request to a periodically querying load balancer before deployment starts)")
- long delayProcessingInSec() default 0;
-
- @AttributeDefinition(name = "Tags to register during delay before processing", description = "List of tags the adhoc result is be registered also during waiting for the configured delay")
- String[] tagsDuringDelayedProcessing() default {};
-
- @AttributeDefinition(name = "Tags to wait for after processing", description = "List of tags to be waited for after processing (leave empty to not wait). While waiting the tags from property 'tags' remain in configured state.")
- String[] waitAfterProcessing_forTags() default {};
-
- @AttributeDefinition(name = "Initial waiting time", description = "Initial waiting time until 'waitAfterProcessing.forTags' are checked for the first time.")
- long waitAfterProcessing_initialWait() default 3;
-
- @AttributeDefinition(name = "Maximum delay after processing", description = "Maximum delay that can be caused when 'waitAfterProcessing.forTags' is configured (waiting is aborted after that time)")
- long waitAfterProcessing_maxDelay() default 120;
-
- @AttributeDefinition
- String webconsole_configurationFactory_nameHint() default "{hc.name} ({osgi.http.whiteboard.filter.regex} {method} {userAgentRegEx}) -> {statusDuringRequestProcessing} for tags {tags} {tagsDuringDelayedProcessing}";
- }
-
- private BundleContext bundleContext;
-
- private Result.Status statusDuringRequestProcessing;
- private String hcNameDuringRequestProcessing;
- private String[] hcTagsDuringRequestProcessing;
-
- private Long delayProcessingInSec;
- private String[] tagsDuringDelayedProcessing;
-
- private String[] waitAfterProcessingForTags;
- private long waitAfterProcessingInitialWait;
- private long waitAfterProcessingMaxDelay;
-
- private String requiredMethod;
- private Pattern userAgentRegEx;
-
-
- @Reference
- private HealthCheckExecutor executor;
-
- @Reference
- ResultTxtVerboseSerializer verboseTxtSerializer;
-
- @Activate
- protected final void activate(ComponentContext context, Config config) {
- this.bundleContext = context.getBundleContext();
- this.statusDuringRequestProcessing = config.statusDuringRequestProcessing();
- this.hcNameDuringRequestProcessing = config.hcName();
- this.hcTagsDuringRequestProcessing = config.tags();
-
- this.delayProcessingInSec = config.delayProcessingInSec() > 0 ? config.delayProcessingInSec(): null;
- this.tagsDuringDelayedProcessing = config.tagsDuringDelayedProcessing();
-
- this.waitAfterProcessingForTags = config.waitAfterProcessing_forTags();
- this.waitAfterProcessingInitialWait = config.waitAfterProcessing_initialWait();
- this.waitAfterProcessingMaxDelay = config.waitAfterProcessing_maxDelay();
-
- this.requiredMethod = StringUtils.defaultIfBlank(config.method(), null);
- this.userAgentRegEx = StringUtils.isNotBlank(config.userAgentRegEx()) ? Pattern.compile(config.userAgentRegEx()) : null;
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
-
- HttpServletRequest httpServletRequest = (HttpServletRequest) request;
- String requestPath = httpServletRequest.getRequestURI();
- String userAgent = httpServletRequest.getHeader("User-Agent");
- String method = httpServletRequest.getMethod();
- boolean isRelevantRequest = /* path is checked by filter pattern */
- (requiredMethod==null || requiredMethod.equals(method))
- && (userAgentRegEx==null || userAgentRegEx.matcher(userAgent).matches());
- if (isRelevantRequest) {
- processRelevantRequest(request, response, filterChain, requestPath);
-
- } else {
- // regular request processing
- filterChain.doFilter(request, response);
- }
- }
-
- private void processRelevantRequest(ServletRequest request, ServletResponse response, FilterChain filterChain, String requestPath)
- throws IOException, ServletException {
-
- AdhocStatusHealthCheck adhocStatusHealthCheck = null;
- try {
-
- String mainHcMsg = "Request "+requestPath+" is being processed";
- if(delayProcessingInSec!=null) {
-
- String hcNameDuringDelay = hcNameDuringRequestProcessing +" (waiting)";
- AdhocStatusHealthCheck adhocStatusHealthCheckDelayedProcessing = null;
- try {
- adhocStatusHealthCheckDelayedProcessing = registerDynamicHealthCheck(statusDuringRequestProcessing, tagsDuringDelayedProcessing, hcNameDuringDelay, "Waiting "+delayProcessingInSec+"sec until continuing request "+requestPath);
-
- LOG.info("Delaying processing of request {} for {}sec", requestPath, delayProcessingInSec);
- try {
- Thread.sleep(delayProcessingInSec * 1000);
- } catch (InterruptedException e) {
- LOG.warn("Exception during delaying processing of request {} for {}sec", requestPath, delayProcessingInSec, e);
- }
- } finally {
- // for the case delayProcessingInSec is set, register regular HC first and then unregister delay HC to ensure there is not even a short time span without result
- adhocStatusHealthCheck = registerDynamicHealthCheck(statusDuringRequestProcessing, hcTagsDuringRequestProcessing, hcNameDuringRequestProcessing, mainHcMsg);
- unregisterDynamicHealthCheck(adhocStatusHealthCheckDelayedProcessing);
- }
- } else {
- adhocStatusHealthCheck = registerDynamicHealthCheck(statusDuringRequestProcessing, hcTagsDuringRequestProcessing, hcNameDuringRequestProcessing, mainHcMsg);
- }
-
- filterChain.doFilter(request, response);
- LOG.info("Request {} is processed", requestPath);
-
- if(waitAfterProcessingForTags.length > 0) {
-
- String initialWaitMsg = "Request "+requestPath +": Waiting for tags "+Arrays.asList(waitAfterProcessingForTags)+": initial wait " + waitAfterProcessingInitialWait+ "sec";
- adhocStatusHealthCheck.updateMessage(initialWaitMsg);
- wait(requestPath, waitAfterProcessingInitialWait * 1000);
-
- long startTime = System.currentTimeMillis();
- for(;;) {
- List<HealthCheckExecutionResult> executionResults = executor.execute(HealthCheckSelector.tags(waitAfterProcessingForTags).withNames("-"+hcNameDuringRequestProcessing), new HealthCheckExecutionOptions().setCombineTagsWithOr(true).setForceInstantExecution(true));
- CombinedExecutionResult combinedExecutionResult = new CombinedExecutionResult(executionResults);
- Result overallResult = combinedExecutionResult.getHealthCheckResult();
- String verboseTxtResult = verboseTxtSerializer.serialize(overallResult, executionResults, false);
-
- String msg = "Request "+requestPath +": Waiting for tags "+Arrays.asList(waitAfterProcessingForTags)+": "+ overallResult.getStatus();
- LOG.info(msg);
- if(LOG.isDebugEnabled()) {
- LOG.debug("\n"+verboseTxtResult);
- }
- adhocStatusHealthCheck.updateMessage(msg);
- if(overallResult.isOk()) {
- break;
- }
- if((System.currentTimeMillis() - startTime) > (waitAfterProcessingMaxDelay*1000)) {
- LOG.warn("Maximum delay time {}sec for tags {} exceeded - continuing anyway", waitAfterProcessingMaxDelay, Arrays.asList(waitAfterProcessingForTags));
- throw new ServletException("Maximum wait time "+waitAfterProcessingMaxDelay+"sec for tags "+Arrays.asList(waitAfterProcessingForTags)+" exceeded:\n"+verboseTxtResult);
- }
-
- LOG.info("Waiting for tags {} before returning from {}", waitAfterProcessingForTags, requestPath);
- wait(requestPath, 500);
- }
- }
-
- } finally {
- unregisterDynamicHealthCheck(adhocStatusHealthCheck);
- }
- }
-
- private void wait(String requestPath, long waitTime) {
- try {
- Thread.sleep(waitTime);
- } catch (InterruptedException e) {
- LOG.warn("Exception during delaying processing of request {} for {}sec", requestPath, delayProcessingInSec, e);
- }
- }
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- // no action required
- }
-
- @Override
- public void destroy() {
- // no action required
- }
-
- private AdhocStatusHealthCheck registerDynamicHealthCheck(Result.Status status, String[] tags, String hcName, String msg) {
- AdhocStatusHealthCheck healthCheck = new AdhocStatusHealthCheck(status, msg);
- Dictionary<String, Object> props = new Hashtable<String, Object>();
- props.put(HealthCheck.NAME, hcName);
- props.put(HealthCheck.TAGS, tags);
-
- ServiceRegistration<HealthCheck> registration = bundleContext.registerService(HealthCheck.class, healthCheck, props);
- healthCheck.setServiceRegistration(registration);
-
- return healthCheck;
- }
-
- private synchronized void unregisterDynamicHealthCheck(AdhocStatusHealthCheck healthCheck) {
- ServiceRegistration<HealthCheck> serviceRegistration = healthCheck !=null ? healthCheck.getServiceRegistration() : null;
- if (serviceRegistration != null) {
- serviceRegistration.unregister();
- LOG.debug("Unregistered adhoc HC");
- }
- }
-
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/filter/ServiceUnavailableFilter.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/filter/ServiceUnavailableFilter.java
deleted file mode 100644
index ddf6701555..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/filter/ServiceUnavailableFilter.java
+++ /dev/null
@@ -1,461 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.filter;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import jakarta.servlet.Filter;
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.FilterConfig;
-import jakarta.servlet.Servlet;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.ServletRequest;
-import jakarta.servlet.ServletResponse;
-import jakarta.servlet.http.HttpServlet;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.executor.CombinedExecutionResult;
-import org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor;
-import org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializer;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.BundleListener;
-import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.framework.startlevel.FrameworkStartLevel;
-import org.osgi.service.component.ComponentContext;
-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.servlet.context.ServletContextHelper;
-import org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Returns a 503 Service Unavailable Page if certain tags are in non-ok result. */
-@Component(service = {} /* Filter registers itself for better control */, immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE)
-@Designate(ocd = ServiceUnavailableFilter.Config.class, factory = true)
-public class ServiceUnavailableFilter implements Filter {
- private static final Logger LOG = LoggerFactory.getLogger(ServiceUnavailableFilter.class);
-
- private static final String CONTENT_TYPE_HTML = "text/html";
- private static final String CONTENT_TYPE_PLAIN = "text/plain";
-
- private static final String CACHE_CONTROL_KEY = "Cache-control";
- private static final String CACHE_CONTROL_VALUE = "no-cache";
-
- private static final String CLASSPATH_PREFIX = "classpath:";
-
- private static final String CONTEXT_NAME = "internal.http.serviceunavailablefilter";
-
- private static final String PROP_STARTUP_CONTEXT_SERVICE_RANKING = "avoid404DuringStartup.contextServiceRanking";
- private static final String PROP_STARTUP_SERVLET_SERVICE_RANKING = "avoid404DuringStartup.servletServiceRanking";
-
-
- @ObjectClassDefinition(name = "Health Check Service Unavailable Filter", description = "Returns a 503 Service Unavailable Page if configured tags are in non-ok result")
- public @interface Config {
-
- String RESPONSE_TEXT_DEFAULT = "<html><head><title>Service Unavailable</title><meta http-equiv=\"refresh\" content=\"5\"></head><body><strong>Service Unavailable</strong></body></html>";
-
- @AttributeDefinition(name = "Filter Request Path RegEx", description = "Regex to be matched against request path. Either use regex or pattern.")
- String osgi_http_whiteboard_filter_regex();
-
- @AttributeDefinition(name = "Filter Context", description = "Needs to be set to correct whiteboard context filter (e.g. '(osgi.http.whiteboard.context.name=default)'")
- String osgi_http_whiteboard_context_select() default "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=*)";
-
- @AttributeDefinition(name = "Tags", description = "List of tags to query the status in order to decide if it is 503 or not")
- String[] tags() default {};
-
- @AttributeDefinition(name = "Status for 503 response", description = "First status that causes a 503 response. The default TEMPORARILY_UNAVAILABLE will not send 503 for OK and WARN but for TEMPORARILY_UNAVAILABLE, CRITICAL and HEALTH_CHECK_ERROR")
- Result.Status statusFor503() default Result.Status.TEMPORARILY_UNAVAILABLE;
-
- @AttributeDefinition(name = "Include execution result in response", description = "Will include the execution result in the response (as html comment for html case, otherwise as text).")
- boolean includeExecutionResult() default false;
-
- @AttributeDefinition(name = "503 Response Text", description = "Response text for 503 responses. Value can be either the content directly or in the format '"+CLASSPATH_PREFIX+"<symbolic-bundle-id>:/path/to/file.html'. The response content type is auto-detected to either text/html or text/plain.")
- String responseTextFor503() default RESPONSE_TEXT_DEFAULT;
-
- @AttributeDefinition(name = "Avoid 404", description = "If true, will automatically register a dummy servlet to ensure this filter becomes effective. Useful for server startup scenarios.")
- boolean avoid404DuringStartup() default true;
-
- @AttributeDefinition(name = "Auto-disable filter", description = "If true, will automatically disable the filter once the filter continued the filter chain without 503 for the first time. Useful for server startup scenarios. Becomes automatically active if avoid404DuringStartup is configured to true.")
- boolean autoDisableFilter() default false;
-
- @AttributeDefinition(name = "Filter Service Ranking", description = "The service.ranking for the filter as respected by http whiteboard.")
- int service_ranking() default Integer.MAX_VALUE;
-
- @AttributeDefinition
- String webconsole_configurationFactory_nameHint() default "Send 503 for tags {tags} at status {statusFor503} (and worse) for path(s) {osgi.http.whiteboard.filter.regex}";
- }
-
- private String[] tags;
- private Result.Status statusFor503;
- private String responseTextFor503;
- private boolean includeExecutionResultInResponse;
- private boolean autoDisableFilter;
- private boolean avoid404DuringStartup;
-
- @Reference
- private ExtendedHealthCheckExecutor executor;
-
- @Reference
- ResultTxtVerboseSerializer verboseTxtSerializer;
-
- private BundleContext bundleContext;
- private Dictionary<String, Object> compProperties;
- private ServiceListener healthCheckServiceListener;
-
- private volatile ServiceRegistration<Filter> filterServiceRegistration;
- private volatile ServiceRegistration<ServletContextHelper> httpContextRegistration;
- private volatile ServiceRegistration<Servlet> defaultServletRegistration;
-
- private volatile ServiceReference<HealthCheck>[] relevantHealthCheckServiceReferences;
-
- @Activate
- protected final void activate(BundleContext bundleContext, ComponentContext componentContext, Config config)
- throws InvalidSyntaxException {
- this.bundleContext = bundleContext;
- this.compProperties = componentContext.getProperties();
-
- this.tags = config.tags();
- this.statusFor503 = config.statusFor503();
- this.responseTextFor503 = getResponseText(bundleContext, config.responseTextFor503());
-
- this.includeExecutionResultInResponse = config.includeExecutionResult();
- this.autoDisableFilter = config.autoDisableFilter();
- this.avoid404DuringStartup = config.avoid404DuringStartup();
- if(avoid404DuringStartup && !autoDisableFilter) {
- LOG.debug("For avoid404DuringStartup set to true, avoid404DuringStartup is forced to true as well");
- autoDisableFilter = true;
- }
-
- registerHealthCheckServiceListener();
-
- selectHcServiceReferences();
- registerFilter();
-
- LOG.info("ServiceUnavailableFilter active (start level {})", getCurrentStartLevel());
- }
-
- @Deactivate
- protected final void deactivate() {
- unregisterHealthCheckServiceListener();
-
- unregisterFilter();
-
- LOG.info("ServiceUnavailableFilter deactivated");
- }
-
-
- String getResponseText(BundleContext bundleContext, String responseFor503) {
- if(StringUtils.isBlank(responseFor503)) {
- responseFor503 = (String) compProperties.get("htmlFor503"); // backwards-compatibility
- }
- if(responseFor503 != null && responseFor503.startsWith(CLASSPATH_PREFIX)) {
- String[] bits = responseFor503.split(":");
- String symbolicName = bits[1];
- String pathInBundle = bits[2];
- Optional<Bundle> bundleOptional = Arrays.stream(bundleContext.getBundles()).filter(b -> b.getSymbolicName().equals(symbolicName)).findFirst();
- if(bundleOptional.isPresent()) {
- URL entryUrl = bundleOptional.get().getEntry(pathInBundle);
- if(entryUrl!=null) {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(entryUrl.openStream(), StandardCharsets.UTF_8))) {
- responseFor503 = reader.lines().collect(Collectors.joining("\n"));
- } catch (Exception e) {
- responseFor503 = "503 Service Unavailable\n(Could not read '"+pathInBundle+"' from bundle '"+symbolicName+"': "+e+")";
- }
- } else {
- responseFor503 = "503 Service Unavailable\n(Could not read '"+pathInBundle+"' from bundle '"+symbolicName+"': file not found)";
- }
- } else {
- responseFor503 = "503 Service Unavailable\n(Could not read '"+pathInBundle+"' from bundle '"+symbolicName+"': bundle not found)";
- }
- }
- return responseFor503;
- }
-
- private int getCurrentStartLevel() {
- return bundleContext.getBundle(Constants.SYSTEM_BUNDLE_ID).adapt(FrameworkStartLevel.class).getStartLevel();
- }
-
- private synchronized void registerFilter() {
- if (filterServiceRegistration == null) {
-
- if (avoid404DuringStartup) {
- registerHttpContext();
- }
-
- filterServiceRegistration = bundleContext.registerService(Filter.class, this, compProperties);
- LOG.debug("Registered ServiceUnavailableFilter for tags {}", Arrays.asList(tags));
-
- if (autoDisableFilter) {
- new UnregisteringFilterThread();
- LOG.debug("Started UnregisteringFilterThread since autoDisableFilter=true");
- }
- }
- }
-
- private synchronized void unregisterFilter() {
- if (filterServiceRegistration != null) {
-
- filterServiceRegistration.unregister();
- filterServiceRegistration = null;
- LOG.debug("Filter ServiceUnavailableFilter for tags {} unregistered", Arrays.asList(tags));
-
- if (avoid404DuringStartup) {
- unregisterHttpContext();
- }
-
- }
- }
-
- // using ServiceListener and ExtendedHealthCheckExecutor to avoid overhead of searching the service references on every request
- private synchronized void selectHcServiceReferences() {
- LOG.debug("Reloading HC references for tags {}", Arrays.asList(tags));
-
- relevantHealthCheckServiceReferences = executor.selectHealthCheckReferences(HealthCheckSelector.tags(tags), new HealthCheckExecutionOptions().setCombineTagsWithOr(true));
- LOG.debug("Found {} health check service references for tags {}", relevantHealthCheckServiceReferences.length, tags);
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
-
- CombinedExecutionResult combinedExecutionResult = executeRelevantChecks(false);
- if (is503Result(combinedExecutionResult)) {
- Result overallResult = combinedExecutionResult.getHealthCheckResult();
- LOG.debug("Result for tags {} is {}, sending 503 for {}", tags, overallResult.getStatus(),
- ((HttpServletRequest) request).getRequestURI());
- String verboseTxtResult = includeExecutionResultInResponse
- ? verboseTxtSerializer.serialize(overallResult, combinedExecutionResult.getExecutionResults(), false)
- : null;
- send503((HttpServletResponse) response, verboseTxtResult);
-
- } else {
- // regular request processing
- filterChain.doFilter(request, response);
- }
- }
-
- private boolean is503Result(CombinedExecutionResult combinedExecutionResult) {
- return combinedExecutionResult.getHealthCheckResult().getStatus().ordinal() >= statusFor503.ordinal();
- }
-
- private CombinedExecutionResult executeRelevantChecks(boolean forceInstantExecution) {
- final long startTimeNs = System.nanoTime();
-
- List<HealthCheckExecutionResult> executionResults = executor.execute(relevantHealthCheckServiceReferences,
- new HealthCheckExecutionOptions().setCombineTagsWithOr(true).setForceInstantExecution(forceInstantExecution));
- CombinedExecutionResult combinedExecutionResult = new CombinedExecutionResult(executionResults, Result.Status.TEMPORARILY_UNAVAILABLE);
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("Time consumed for executing checks: {}ns", System.nanoTime() - startTimeNs);
- }
- return combinedExecutionResult;
- }
-
- private void send503(HttpServletResponse response, String verboseTxtResult) throws IOException {
-
- if(avoid404DuringStartup && LOG.isDebugEnabled()) {
- LOG.debug("Sending 503 at start level {}", getCurrentStartLevel());
- }
-
- String responseContent = responseTextFor503;
- boolean isHtml = responseContent.contains("<html");
- String bodyClosingTag = "</body>";
-
- if(verboseTxtResult != null) {
- if(isHtml) {
- responseContent = responseContent.replace(bodyClosingTag, "<!--\n\n" + verboseTxtResult + "\n\n-->" + bodyClosingTag);
- } else {
- responseContent += "\n" + verboseTxtResult;
- }
- }
-
- response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
- response.setContentType(isHtml ? CONTENT_TYPE_HTML : CONTENT_TYPE_PLAIN);
- response.setHeader(CACHE_CONTROL_KEY, CACHE_CONTROL_VALUE);
- response.setCharacterEncoding("UTF-8");
- response.getWriter().append(responseContent);
- }
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- // no action required
- }
-
- @Override
- public void destroy() {
- // no action required
- }
-
-
- // --- HealthCheckServiceListener (to ensure current set of HC service references is up to date
-
- private synchronized void registerHealthCheckServiceListener() throws InvalidSyntaxException {
- if (healthCheckServiceListener == null) {
- healthCheckServiceListener = new HealthCheckServiceListener();
- bundleContext.addServiceListener(healthCheckServiceListener, "(objectclass=" + HealthCheck.class.getName() + ")");
- }
- }
-
- private synchronized void unregisterHealthCheckServiceListener() {
- if (healthCheckServiceListener != null) {
- bundleContext.removeServiceListener(healthCheckServiceListener);
- }
- }
-
- private final class HealthCheckServiceListener implements ServiceListener {
- @Override
- public void serviceChanged(ServiceEvent event) {
-
- LOG.debug("Service Event for Health Check: {}", event.getType());
- selectHcServiceReferences();
- }
- }
-
- // --- http context for avoid404DuringStartup=true
-
- private void registerHttpContext() {
-
- final Dictionary<String, Object> properties = new Hashtable<>();
- properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME, CONTEXT_NAME);
- properties.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_PATH, "/");
- Object contextServiceRanking = compProperties.get(PROP_STARTUP_CONTEXT_SERVICE_RANKING);
- properties.put(Constants.SERVICE_RANKING, contextServiceRanking!=null ? contextServiceRanking : Integer.MAX_VALUE);
-
- this.httpContextRegistration = bundleContext.registerService(ServletContextHelper.class, new ServletContextHelper() {
- }, properties);
-
- final Dictionary<String, Object> servletProps = new Hashtable<>();
- servletProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
- "(" + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "=" + CONTEXT_NAME + ")");
- servletProps.put(HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN, "/");
- Object servletServiceRanking = compProperties.get(PROP_STARTUP_SERVLET_SERVICE_RANKING);
- servletProps.put(Constants.SERVICE_RANKING, servletServiceRanking!=null ? servletServiceRanking : 0);
-
- this.defaultServletRegistration = bundleContext.registerService(Servlet.class, new HttpServlet() {
- private static final long serialVersionUID = 1L;
-
- @Override
- protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- send503((HttpServletResponse) resp, "Service Unavailable");
- }
-
- }, servletProps);
- }
-
- private void unregisterHttpContext() {
- if (this.defaultServletRegistration != null) {
- this.defaultServletRegistration.unregister();
- this.defaultServletRegistration = null;
- }
- if (this.httpContextRegistration != null) {
- this.httpContextRegistration.unregister();
- this.httpContextRegistration = null;
- }
- }
-
- // --- handling for autoDisableFilter=true
-
- public class UnregisteringFilterThread extends Thread {
-
- UnregisteringFilterThread() {
- setDaemon(true);
- setName("UnregisteringFilterThread for ServiceUnavailableFilter with tags "+Arrays.asList(tags));
- start();
- }
-
- @Override
- public void run() {
-
- while(autoDisableFilter && filterServiceRegistration != null) {
- CombinedExecutionResult executionResult = executeRelevantChecks(true);
- boolean is503Result = is503Result(executionResult);
-
- if(!is503Result) {
- unregisterFilter();
- int startLevelWhenFilterUnregistered = getCurrentStartLevel();
- LOG.debug("Unregistered filter ServiceUnavailableFilter for tags {} since result was ok at start level {}", Arrays.asList(tags), startLevelWhenFilterUnregistered);
-
- new BundleListenerForReregisteringFilter(bundleContext, startLevelWhenFilterUnregistered);
-
- break;
- }
-
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- LOG.debug("UnregisteringFilterThread for tags {} was interrupted", Arrays.asList(tags));
- break;
- }
-
- }
- }
- }
-
- private final class BundleListenerForReregisteringFilter implements BundleListener {
- private final Logger LOG = LoggerFactory.getLogger(getClass());
-
- private final int startLevelWhenFilterUnregistered;
-
- public BundleListenerForReregisteringFilter(BundleContext bundleContext, int startLevelWhenFilterUnregistered) {
- this.startLevelWhenFilterUnregistered = startLevelWhenFilterUnregistered;
- LOG.debug("BundleListenerForReregisteringFilter registered");
- bundleContext.addBundleListener(this);
- }
-
- @Override
- public void bundleChanged(BundleEvent event) {
- int currentStartLevel = getCurrentStartLevel();
- if(currentStartLevel != startLevelWhenFilterUnregistered) {
- LOG.debug("Start level changed (current={} previous={}) - reregistering filter", currentStartLevel, startLevelWhenFilterUnregistered);
- registerFilter();
- bundleContext.removeBundleListener(this);
- LOG.debug("Removed self from BundleListeners");
- }
- }
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
deleted file mode 100644
index 5e335e35cd..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitor.java
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.monitor;
-
-import static java.util.stream.Collectors.toList;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.condition.Healthy;
-import org.apache.felix.hc.api.condition.SystemReady;
-import org.apache.felix.hc.api.condition.Unhealthy;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.executor.CombinedExecutionResult;
-import org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor;
-import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.apache.felix.hc.core.impl.scheduling.AsyncIntervalJob;
-import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.apache.felix.hc.core.impl.scheduling.CronJobFactory;
-import org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializer;
-import org.apache.felix.hc.core.impl.util.HealthCheckFilter;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.ComponentConstants;
-import org.osgi.service.component.ComponentContext;
-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.event.EventAdmin;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * <p>Monitors health check tags and/or names and depending on configuration:</p>
- * <ul>
- * <li>Activates the condition marker services {@link SystemReady},
- * {@link Healthy}, {@link Unhealthy}</li>
- * <li>Sends OSGi events</li>
- * </ul>
- *
- */
-@Component(immediate = true, configurationPolicy = ConfigurationPolicy.REQUIRE)
-@Designate(ocd = HealthCheckMonitor.Config.class, factory = true)
-public class HealthCheckMonitor implements Runnable {
- private static final Logger LOG = LoggerFactory.getLogger(HealthCheckMonitor.class);
-
- public enum ChangeType {
- NONE, STATUS_CHANGES, STATUS_CHANGES_OR_NOT_OK, ALL
- }
-
- @ObjectClassDefinition(name = "Health Check Monitor", description = "Regularly executes health checks according to given interval/cron expression")
- public @interface Config {
-
- @AttributeDefinition(name = "Tags", description = "List of tags to monitor")
- String[] tags() default {};
-
- @AttributeDefinition(name = "Names", description = "List of health check names to monitor")
- String[] names() default {};
-
- @AttributeDefinition(name = "Interval (Sec)", description = "Will execute the checks for given tags/names every n seconds (either use intervalInSec or cronExpression )")
- long intervalInSec() default 0;
-
- @AttributeDefinition(name = "Interval (Cron Expresson)", description = "Will execute the checks for given tags/names according to cron expression")
- String cronExpression() default "";
-
- @AttributeDefinition(name = "Register Healthy Marker Service", description = "For the case a given tag/name is healthy, will register a service Healthy with property tag=<tagname> (or name=<hc.name>) that other services can depend on")
- boolean registerHealthyMarkerService() default true;
-
- @AttributeDefinition(name = "Register Unhealthy Marker Service", description = "For the case a given tag/name is unhealthy, will register a service Unhealthy with property tag=<tagname> (or name=<hc.name>) that other services can depend on")
- boolean registerUnhealthyMarkerService() default false;
-
- @AttributeDefinition(name = "Treat WARN as Healthy", description = "Whether to treat status WARN as healthy (defaults to true because WARN indicates a working system that only possibly might become unavailable if no action is taken)")
- boolean treatWarnAsHealthy() default true;
-
- @AttributeDefinition(name = "Send Events", description = "What updates should be sent as OSGi events (none, status changes, status changes and not ok results, all updates)")
- ChangeType sendEvents() default ChangeType.STATUS_CHANGES;
-
- @AttributeDefinition(name = "Log results", description = "What updates should be logged to regular log file (none, status changes, status changes and not ok results, all updates)")
- ChangeType logResults() default ChangeType.NONE;
-
- @AttributeDefinition(name = "Log all results as INFO", description = "If logResults is enabled and this is enabled, all results will be logged with INFO log level. Otherwise WARN and INFO are used depending on the health state.")
- boolean logAllResultsAsInfo() default false;
-
- @AttributeDefinition(name = "Resolve Tags (dynamic)", description = "In dynamic mode tags are resolved to a list of health checks that are monitored individually (this means events are sent/services are registered for name only, never for given tags). This mode allows to use '*' in tags to query for all health checks in system. It is also possible to query for all except certain tags by using '-', e.g. by configuring the values '*', '-tag1' and '-tag2' for tags.")
- boolean isDynamic() default false;
-
- @AttributeDefinition
- String webconsole_configurationFactory_nameHint() default "Health Monitor for '{tags}'/'{names}', {intervalInSec}sec/{cronExpression}, Marker Service Healthy:{registerHealthyMarkerService} Unhealthy:{registerUnhealthyMarkerService}, Send Events {sendEvents}";
- }
-
- @Reference
- ExtendedHealthCheckExecutor executor;
-
- @Reference
- HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
-
- @Reference
- ResultTxtVerboseSerializer resultTxtVerboseSerializer;
-
- @Reference
- CronJobFactory cronJobFactory;
-
- @Reference
- private EventAdmin eventAdmin;
-
- // component state
- AsyncJob monitorJob = null;
- List<String> tags;
- List<String> names;
- Map<Object,HealthState> healthStates = new ConcurrentHashMap<>();
-
- private long intervalInSec;
- private String cronExpression;
-
- private boolean registerHealthyMarkerService;
- private boolean registerUnhealthyMarkerService;
-
- private boolean treatWarnAsHealthy;
-
- private ChangeType sendEvents;
- private ChangeType logResults;
-
- private boolean logAllResultsAsInfo;
-
- private BundleContext bundleContext;
-
- private String monitorId;
-
- private boolean isDynamic;
- private ServiceListener healthCheckServiceListener;
-
- @Activate
- protected final void activate(BundleContext bundleContext, Config config, ComponentContext componentContext) throws InvalidSyntaxException {
-
- this.bundleContext = bundleContext;
-
- this.tags = Arrays.stream(config.tags()).filter(StringUtils::isNotBlank).collect(toList());
- this.names = Arrays.stream(config.names()).filter(StringUtils::isNotBlank).collect(toList());
- this.isDynamic = config.isDynamic();
- initHealthStates();
-
- this.registerHealthyMarkerService = config.registerHealthyMarkerService();
- this.registerUnhealthyMarkerService = config.registerUnhealthyMarkerService();
-
- this.treatWarnAsHealthy = config.treatWarnAsHealthy();
- this.sendEvents = config.sendEvents();
- this.logResults = config.logResults();
- this.logAllResultsAsInfo = config.logAllResultsAsInfo();
-
- this.intervalInSec = config.intervalInSec();
- this.cronExpression = config.cronExpression();
-
- this.monitorId = getMonitorId(componentContext.getProperties().get(ComponentConstants.COMPONENT_ID));
- if (StringUtils.isNotBlank(cronExpression)) {
- monitorJob = cronJobFactory.createAsyncCronJob(this, monitorId, "healthcheck-monitor", cronExpression);
- } else if (intervalInSec > 0) {
- monitorJob = new AsyncIntervalJob(this, healthCheckExecutorThreadPool, intervalInSec);
- } else {
- throw new IllegalArgumentException("Either cronExpression or intervalInSec needs to be set");
- }
- monitorJob.schedule();
- LOG.info("Monitor active for tags {} and names {} (isDynamic={})", this.tags, this.names, this.isDynamic);
- }
-
- private void initHealthStates() throws InvalidSyntaxException {
- if(!this.isDynamic) {
- this.tags.stream().filter(StringUtils::isNotBlank).forEach(tag -> {
- if(tag.contains("*") || tag.startsWith("-")) {
- throw new IllegalArgumentException("Health check monitor is configured to isDyamic=false but tags contain query items like '*' or '-': "+String.join(",", this.tags));
- }
- healthStates.put(tag, new HealthState(this, tag, true));
- });
- this.names.stream().filter(StringUtils::isNotBlank).forEach(name -> {
- healthStates.put(name, new HealthState(this, name, false));
- });
- } else {
- updateHealthStatesMap();
- healthCheckServiceListener = new HealthCheckServiceListener();
- bundleContext.addServiceListener(healthCheckServiceListener, HealthCheckFilter.HC_FILTER_OBJECT_CLASS);
- }
-
- }
-
- private String getMonitorId(Object compId) {
- return "hc-monitor-" + compId + '-' + String.join(",", tags)+(!names.isEmpty()?"-"+names.size()+"_names":"");
- }
-
- @Override
- public String toString() {
- return "[HealthCheckMonitor tags=" + tags + "/names=" + names + ", intervalInSec=" + intervalInSec + "/cron="
- + cronExpression + "]";
- }
-
- @Deactivate
- protected final void deactivate() {
- if(healthCheckServiceListener != null) {
- bundleContext.removeServiceListener(healthCheckServiceListener);
- }
- healthStates.values().stream().forEach(HealthState::cleanUp);
- healthStates.clear();
- monitorJob.unschedule();
- LOG.info("Monitor deactivated for tags {} and names {}", this.tags, this.names);
- }
-
- public void updateHealthStatesMap() {
- HealthCheckFilter filter = new HealthCheckFilter(bundleContext);
- HealthCheckSelector selector = HealthCheckSelector.tags(tags.toArray(new String[tags.size()])).withNames(names.toArray(new String[names.size()]));
- ServiceReference<HealthCheck>[] refs = filter.getHealthCheckServiceReferences(selector, true);
- LOG.debug("Found {} health check service refs", refs.length);
- List<Object> oldServiceIds = new ArrayList<>(healthStates.keySet()); // start with all keys
- for (ServiceReference<HealthCheck> ref : refs) {
- Long serviceId = (Long) ref.getProperty(Constants.SERVICE_ID);
- if(healthStates.containsKey(serviceId)) {
- // HC state exists, keep
- oldServiceIds.remove(serviceId);
- } else {
- // add HC state
- HealthState healthState = new HealthState(this, ref);
- LOG.debug("Monitoring health state: {}", healthState);
- healthStates.put(serviceId, healthState);
- }
- }
- // Remove obsolete HC states
- for (Object oldServiceId : oldServiceIds) {
- HealthState removed = healthStates.remove(oldServiceId);
- removed.cleanUp();
- LOG.debug("Removed monitoring for health state: {}", removed);
- }
- }
-
- public void run() {
- runWithThreadNameContext(() -> {
- try {
-
- // run in tags/names in parallel
- healthStates.values().parallelStream().forEach(healthState ->
- runWithThreadNameContext(healthState::update)
- );
-
- if(logResults != ChangeType.NONE) {
- logResults();
- }
-
- LOG.debug("Updated {} health states for tags {} and names {}", healthStates.size(), this.tags, this.names);
- } catch (Exception e) {
- LOG.error("Exception during execution of checks in HealthCheckMonitor: " + e, e);
- }
- });
- }
-
- private void logResults() {
-
- for(HealthState healthState: healthStates.values()) {
-
- HealthCheckExecutionResult executionResult = healthState.getExecutionResult();
-
- boolean isOk = executionResult.getHealthCheckResult().isOk();
- if(!LOG.isInfoEnabled() && isOk) {
- return; // with INFO disabled even ChangeType.ALL would not log it
- }
- boolean changeToBeLogged = healthState.hasChanged() && (logResults == ChangeType.STATUS_CHANGES || logResults == ChangeType.STATUS_CHANGES_OR_NOT_OK);
- boolean notOkToBeLogged = !isOk && logResults == ChangeType.STATUS_CHANGES_OR_NOT_OK;
- if(!changeToBeLogged && !notOkToBeLogged && logResults != ChangeType.ALL) {
- continue;
- }
-
- List<HealthCheckExecutionResult> execResults;
- boolean isCombinedResult = executionResult instanceof CombinedExecutionResult;
- if (isCombinedResult) {
- execResults = ((CombinedExecutionResult) executionResult).getExecutionResults();
- } else {
- execResults = Arrays.asList(executionResult);
- }
-
- String label =
- isCombinedResult ? String.format("Health State for %s '%s': healthy:%b isOk:%b hasChanged:%b count HCs:%d", (healthState.isTag() ? "tag" : "name"), healthState.getTagOrName(), healthState.isHealthy(), isOk, healthState.hasChanged(), execResults.size())
- : String.format("Health State for '%s': healthy:%b hasChanged:%b", executionResult.getHealthCheckMetadata().getTitle(), healthState.isHealthy(), healthState.hasChanged());
- if(!healthState.hasChanged() && notOkToBeLogged) {
- // filter the ok items to not clutter the log file
- execResults = execResults.stream().filter(r -> !r.getHealthCheckResult().isOk()).collect(toList());
- }
-
- String logMsg = resultTxtVerboseSerializer.serialize(label, execResults, false);
- logResultItem(isOk, logMsg);
- }
-
- }
-
- void logResultItem(boolean isOk, String msg) {
- if(isOk || this.logAllResultsAsInfo) {
- LOG.info(msg);
- } else {
- LOG.warn(msg);
- }
- }
-
- private void runWithThreadNameContext(Runnable r) {
- String threadNameToRestore = Thread.currentThread().getName();
- try {
- Thread.currentThread().setName(monitorId);
- r.run();
- } finally {
- Thread.currentThread().setName(threadNameToRestore);
- }
- }
-
-
- ExtendedHealthCheckExecutor getExecutor() {
- return executor;
- }
-
- EventAdmin getEventAdmin() {
- return eventAdmin;
- }
-
- boolean isRegisterHealthyMarkerService() {
- return registerHealthyMarkerService;
- }
-
- boolean isRegisterUnhealthyMarkerService() {
- return registerUnhealthyMarkerService;
- }
-
- ChangeType getSendEvents() {
- return sendEvents;
- }
-
- BundleContext getBundleContext() {
- return bundleContext;
- }
-
- boolean isTreatWarnAsHealthy() {
- return treatWarnAsHealthy;
- }
-
- private final class HealthCheckServiceListener implements ServiceListener {
- @Override
- public void serviceChanged(ServiceEvent event) {
- updateHealthStatesMap();
- }
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthState.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthState.java
deleted file mode 100644
index 3f2c3452db..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/monitor/HealthState.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.monitor;
-
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.condition.Healthy;
-import org.apache.felix.hc.api.condition.SystemReady;
-import org.apache.felix.hc.api.condition.Unhealthy;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.executor.CombinedExecutionResult;
-import org.apache.felix.hc.core.impl.monitor.HealthCheckMonitor.ChangeType;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.component.ComponentConstants;
-import org.osgi.service.event.Event;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class HealthState {
- private static final Logger LOG = LoggerFactory.getLogger(HealthState.class);
-
- public static final String TAG_SYSTEMREADY = "systemready";
-
- public static final String EVENT_TOPIC_PREFIX = "org/apache/felix/health";
- public static final String EVENT_TOPIC_SUFFIX_STATUS_CHANGED = "STATUS_CHANGED";
- public static final String EVENT_TOPIC_SUFFIX_STATUS_UPDATED = "UPDATED";
-
- public static final String EVENT_PROP_EXECUTION_RESULT = "executionResult";
- public static final String EVENT_PROP_STATUS = "status";
- public static final String EVENT_PROP_PREVIOUS_STATUS = "previousStatus";
-
- static final Healthy MARKER_SERVICE_HEALTHY = new Healthy() {
- };
- static final Unhealthy MARKER_SERVICE_UNHEALTHY = new Unhealthy() {
- };
- static final SystemReady MARKER_SERVICE_SYSTEMREADY = new SystemReady() {
- };
-
- private final HealthCheckMonitor monitor;
-
- private final String tagOrName;
- private final ServiceReference<HealthCheck> healthCheckRef;
- private final boolean isTag;
- private final String propertyName;
-
- private ServiceRegistration<?> healthyRegistration = null;
- private ServiceRegistration<Unhealthy> unhealthyRegistration = null;
-
- private HealthCheckExecutionResult executionResult;
- private Result.Status status = null;
- private boolean isHealthy = false;
- private boolean statusChanged = false;
- private boolean isLive = true;
-
- HealthState(HealthCheckMonitor healthCheckMonitor, ServiceReference<HealthCheck> ref) {
- this.monitor = healthCheckMonitor;
- HealthCheckMetadata metadata = new HealthCheckMetadata(ref);
- this.tagOrName = metadata.getTitle(); // using title here to ensure it is never null, usually title is nc.name
- this.healthCheckRef = ref;
- this.isTag = false;
- this.propertyName = this.isTag ? "tag" : "name";
- }
-
- HealthState(HealthCheckMonitor healthCheckMonitor, String tagOrName, boolean isTag) {
- this.monitor = healthCheckMonitor;
- this.tagOrName = tagOrName;
- this.healthCheckRef = null;
- this.isTag = isTag;
- this.propertyName = isTag ? "tag" : "name";
- }
-
- @Override
- public String toString() {
- return "[HealthState tagOrName=" + tagOrName + (healthCheckRef!=null?" (service ref)":"") + ", isTag=" + isTag + ", status=" + status + ", isHealthy="
- + isHealthy + ", statusChanged=" + statusChanged + "]";
- }
-
- public boolean hasChanged() {
- return statusChanged;
- }
-
- public boolean isHealthy() {
- return isHealthy;
- }
-
- String getTagOrName() {
- return tagOrName;
- }
-
- boolean isTag() {
- return isTag;
- }
-
- HealthCheckExecutionResult getExecutionResult() {
- return executionResult;
- }
-
- public void update() {
-
- List<HealthCheckExecutionResult> executionResults;
- if(healthCheckRef != null) {
- executionResults = Arrays.asList(monitor.getExecutor().execute(healthCheckRef));
- } else {
- HealthCheckSelector selector = isTag ? HealthCheckSelector.tags(tagOrName) : HealthCheckSelector.names(tagOrName);
- executionResults = monitor.getExecutor().execute(selector);
- }
-
- HealthCheckExecutionResult result = executionResults.size() == 1 ? executionResults.get(0)
- : new CombinedExecutionResult(executionResults, Result.Status.TEMPORARILY_UNAVAILABLE);
- LOG.trace("Result of '{}' => {}", tagOrName, result.getHealthCheckResult().getStatus());
-
- update(result);
-
- }
-
- synchronized void update(HealthCheckExecutionResult executionResult) {
- if(!isLive) {
- LOG.trace("Not live anymore, skipping result update for {}", this);
- return;
- }
- this.executionResult = executionResult;
- Result.Status previousStatus = status;
- status = executionResult.getHealthCheckResult().getStatus();
-
- isHealthy = (status == Result.Status.OK || (monitor.isTreatWarnAsHealthy() && status == Result.Status.WARN));
- statusChanged = previousStatus != status;
- LOG.trace(" {}: isHealthy={} statusChanged={}", tagOrName, isHealthy, statusChanged);
-
- registerMarkerServices();
- sendEvents(executionResult, previousStatus);
- }
-
- private void registerMarkerServices() {
- if (monitor.isRegisterHealthyMarkerService()) {
- if (isHealthy && healthyRegistration == null) {
- registerHealthyService();
- } else if (!isHealthy && healthyRegistration != null) {
- unregisterHealthyService();
- }
- }
- if (monitor.isRegisterUnhealthyMarkerService()) {
- if (!isHealthy && unhealthyRegistration == null) {
- registerUnhealthyService();
- } else if (isHealthy && unhealthyRegistration != null) {
- unregisterUnhealthyService();
- }
- }
- }
-
- private void registerHealthyService() {
- if (healthyRegistration == null) {
- LOG.debug("HealthCheckMonitor: registerHealthyService() {} ", tagOrName);
- Dictionary<String, String> registrationProps = new Hashtable<>();
- registrationProps.put(propertyName, tagOrName);
- registrationProps.put("activated", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
-
- if (TAG_SYSTEMREADY.equals(tagOrName)) {
- LOG.debug("HealthCheckMonitor: SYSTEM READY");
- healthyRegistration = monitor.getBundleContext().registerService(
- new String[] { SystemReady.class.getName(), Healthy.class.getName() },
- MARKER_SERVICE_SYSTEMREADY, registrationProps);
- } else {
- healthyRegistration = monitor.getBundleContext().registerService(Healthy.class, MARKER_SERVICE_HEALTHY,
- registrationProps);
- }
- LOG.debug("HealthCheckMonitor: Healthy service for {} '{}' registered", propertyName, tagOrName);
- }
- }
-
- private void unregisterHealthyService() {
- if (healthyRegistration != null) {
- healthyRegistration.unregister();
- healthyRegistration = null;
- LOG.debug("HealthCheckMonitor: Healthy service for {} '{}' unregistered", propertyName, tagOrName);
- }
- }
-
- private void registerUnhealthyService() {
- if (unhealthyRegistration == null) {
- Dictionary<String, String> registrationProps = new Hashtable<>();
- registrationProps.put("tag", tagOrName);
- registrationProps.put("activated", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
- unhealthyRegistration = monitor.getBundleContext().registerService(Unhealthy.class, MARKER_SERVICE_UNHEALTHY,
- registrationProps);
- LOG.debug("HealthCheckMonitor: Unhealthy service for {} '{}' registered", propertyName, tagOrName);
- }
- }
-
- private void unregisterUnhealthyService() {
- if (unhealthyRegistration != null) {
- unhealthyRegistration.unregister();
- unhealthyRegistration = null;
- LOG.debug("HealthCheckMonitor: Unhealthy service for {} '{}' unregistered", propertyName, tagOrName);
- }
- }
-
- private void sendEvents(HealthCheckExecutionResult executionResult, Result.Status previousStatus) {
- ChangeType sendEventsConfig = monitor.getSendEvents();
- if (sendEventsConfig == ChangeType.ALL
- || (statusChanged && (sendEventsConfig == ChangeType.STATUS_CHANGES || sendEventsConfig == ChangeType.STATUS_CHANGES_OR_NOT_OK))
- || (!executionResult.getHealthCheckResult().isOk() && sendEventsConfig == ChangeType.STATUS_CHANGES_OR_NOT_OK)) {
-
- String eventSuffix = statusChanged ? EVENT_TOPIC_SUFFIX_STATUS_CHANGED : EVENT_TOPIC_SUFFIX_STATUS_UPDATED;
- String logMsg = "Posted event for topic '{}': " + (statusChanged ? "Status change from {} to {}" : "Result updated (status {})");
-
- Map<String, Object> properties = new HashMap<>();
- properties.put(EVENT_PROP_STATUS, status);
- if (previousStatus != null) {
- properties.put(EVENT_PROP_PREVIOUS_STATUS, previousStatus);
- }
- properties.put(EVENT_PROP_EXECUTION_RESULT, executionResult);
- String topic = String.join("/", EVENT_TOPIC_PREFIX, propertyName, tagOrName.replaceAll("[^A-Za-z0-9-_]+", "_"), eventSuffix);
- monitor.getEventAdmin().postEvent(new Event(topic, properties));
- LOG.debug(logMsg, topic, previousStatus, status);
- if (!(executionResult instanceof CombinedExecutionResult)) {
- String componentName = (String) executionResult.getHealthCheckMetadata().getServiceReference()
- .getProperty(ComponentConstants.COMPONENT_NAME);
- if (StringUtils.isNotBlank(componentName)) {
- String topicClass = String.join("/", EVENT_TOPIC_PREFIX, "component", componentName.replace(".", "/"), eventSuffix);
- monitor.getEventAdmin().postEvent(new Event(topicClass, properties));
- LOG.debug(logMsg, topicClass, previousStatus, status);
- }
- }
- }
- }
-
- synchronized void cleanUp() {
- unregisterHealthyService();
- unregisterUnhealthyService();
- isLive = false;
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/AsyncIntervalJob.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/AsyncIntervalJob.java
deleted file mode 100644
index a5758b4a7c..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/AsyncIntervalJob.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling;
-
-import java.util.concurrent.ScheduledFuture;
-
-import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Runs health checks that are configured with an interval (ScheduledThreadPoolExecutor.scheduleAtFixedRate()) for asynchronous execution. */
-public class AsyncIntervalJob extends AsyncJob {
- private static final Logger LOG = LoggerFactory.getLogger(AsyncIntervalJob.class);
-
- private final HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
- private final Long asyncIntervalInSec;
-
- private ScheduledFuture<?> scheduleFuture = null;
-
- public AsyncIntervalJob(Runnable runnable, HealthCheckExecutorThreadPool healthCheckExecutorThreadPool, Long asyncIntervalInSec) {
- super(runnable);
- this.healthCheckExecutorThreadPool = healthCheckExecutorThreadPool;
- this.asyncIntervalInSec = asyncIntervalInSec;
- }
-
- public boolean schedule() {
- scheduleFuture = healthCheckExecutorThreadPool.scheduleAtFixedRate(runnable, asyncIntervalInSec);
- LOG.info("Scheduled job {} for execution every {}sec", this, asyncIntervalInSec);
- return true;
- }
-
- @Override
- public boolean unschedule() {
-
- if (scheduleFuture != null) {
- LOG.debug("Unscheduling async job for {}", runnable);
- return scheduleFuture.cancel(false);
- } else {
- LOG.debug("No scheduled future for {} exists", runnable);
- return false;
- }
- }
-
- @Override
- public String toString() {
- return "[Async interval job for " + runnable + "]";
- }
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/AsyncJob.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/AsyncJob.java
deleted file mode 100644
index 18d5c68924..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/AsyncJob.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling;
-
-/** Abstract class for async scheduling variants cron/interval. */
-public abstract class AsyncJob {
-
- protected final Runnable runnable;
-
- public AsyncJob(Runnable runnable) {
- this.runnable = runnable;
- }
-
- public abstract boolean schedule();
-
- public abstract boolean unschedule();
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/CronJobFactory.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/CronJobFactory.java
deleted file mode 100644
index cb42bc7551..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/CronJobFactory.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling;
-
-import static org.osgi.framework.namespace.PackageNamespace.PACKAGE_NAMESPACE;
-
-import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.apache.felix.hc.core.impl.scheduling.cron.embedded.AsyncEmbeddedCronJob;
-import org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronSchedulerProvider;
-import org.apache.felix.hc.core.impl.scheduling.cron.quartz.AsyncQuartzCronJob;
-import org.apache.felix.hc.core.impl.scheduling.cron.quartz.QuartzCronSchedulerProvider;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.wiring.BundleWire;
-import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Component without direct quartz imports (can always start) that will provide a QuartzCronScheduler on demand. */
-@Component(service = CronJobFactory.class)
-public class CronJobFactory {
- private static final Logger LOG = LoggerFactory.getLogger(CronJobFactory.class);
-
- private static final String QUARTZ_PACKAGE_PREFIX = "org.quartz";
-
- @Reference
- private HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
-
- @Reference
- private QuartzCronSchedulerProvider quartzCronSchedulerProvider;
-
- @Reference
- private EmbeddedCronSchedulerProvider embeddedCronSchedulerProvider;
-
- boolean isQuartzAvailable;
-
- @Activate
- protected synchronized void activate(BundleContext bundleContext) {
- isQuartzAvailable = isQuartzAvailable(bundleContext);
- LOG.info("Quartz is " + (isQuartzAvailable?"":"NOT ")+"available for scheduling cron jobs");
- }
-
- public AsyncJob createAsyncCronJob(Runnable runnable, String id, String group, String cronExpression) {
-
- if(isQuartzAvailable) {
- return new AsyncQuartzCronJob(runnable, quartzCronSchedulerProvider, id, group, cronExpression);
- } else {
- return new AsyncEmbeddedCronJob(runnable, embeddedCronSchedulerProvider, id, cronExpression);
- }
- }
-
- private boolean isQuartzAvailable(BundleContext bundleContext) {
- final BundleWiring wiring = bundleContext.getBundle().adapt(BundleWiring.class);
- for (final BundleWire wire : wiring.getRequiredWires(PACKAGE_NAMESPACE)) {
- final String pkg = (String) wire.getCapability().getAttributes().get(PACKAGE_NAMESPACE);
- if (pkg.startsWith(QUARTZ_PACKAGE_PREFIX)) {
- return true;
- }
- }
- return false;
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/AsyncEmbeddedCronJob.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/AsyncEmbeddedCronJob.java
deleted file mode 100644
index b0ca4e4c5b..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/AsyncEmbeddedCronJob.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.RejectedExecutionException;
-
-import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Runs health checks/monitors that are configured with a cron expression for
- * asynchronous execution.
- */
-public final class AsyncEmbeddedCronJob extends AsyncJob {
-
- private static final Logger LOG = LoggerFactory.getLogger(AsyncEmbeddedCronJob.class);
-
- private final String id;
- private final String cronExpression;
- private final EmbeddedCronParser cronParser;
-
- private final EmbeddedCronScheduler scheduler;
-
- private volatile boolean executing = false;
- private volatile long lastExecutingTime = 0;
- private volatile long nextExecutingTime = 0;
-
- public AsyncEmbeddedCronJob(final Runnable runnable, final EmbeddedCronSchedulerProvider embeddedCronSchedulerProvider,
- final String id, final String cronExpression) {
- super(runnable);
- this.id = id;
-
- this.scheduler = embeddedCronSchedulerProvider.getScheduler();
-
- this.cronExpression = cronExpression;
- this.cronParser= new EmbeddedCronParser(cronExpression);
- nextExecutingTime = cronParser.next(System.currentTimeMillis());
- }
-
- @Override
- public boolean schedule() {
- try {
- scheduler.schedule(this);
- LOG.info("Scheduled job {} with trigger {}", id, cronExpression);
- return true;
- } catch (final Exception e) {
- LOG.error("Could not schedule job for {}", runnable, e);
- return false;
- }
- }
-
- @Override
- public boolean unschedule() {
-
- LOG.debug("Unscheduling job {}", id);
- try {
- scheduler.remove(this);
- return true;
- } catch (final Exception e) {
- LOG.error("Could not unschedule job for {}", id, e);
- return false;
- }
- }
-
- @Override
- public String toString() {
- return "[Async embedded cron job for " + runnable + "]";
- }
-
- public String getId() {
- return id;
- }
-
- public long getLastExecutingTime() {
- return lastExecutingTime;
- }
-
- public long getNextExecutingTime() {
- return nextExecutingTime;
- }
-
- public boolean isExecuting() {
- return executing;
- }
-
- public void checkAndExecute(ExecutorService tasksExecutor) {
- final long currentTime = System.currentTimeMillis();
- if (nextExecutingTime >= currentTime) {
- return;
- }
- if(executing) {
- nextExecutingTime = cronParser.next(System.currentTimeMillis());
- LOG.trace("Cron task {} would be fired but is still executing, skipping this execution, next execution: {}", id, nextExecutingTime);
- return;
- }
-
- try {
- lastExecutingTime = currentTime;
- executing = true;
- tasksExecutor.execute(() -> {
- try {
- runnable.run();
- } catch (final Exception e) {
- LOG.error("Exception while executing cron task {}", id, e);
- } finally {
- nextExecutingTime = cronParser.next(System.currentTimeMillis());
- executing = false;
- }
- });
-
- } catch (final RejectedExecutionException e) {
- executing = false;
- LOG.error("Failed to start cron task: {}", id, e);
- }
- }
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParser.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParser.java
deleted file mode 100644
index ddec1e3005..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParser.java
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
-
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.TimeZone;
-import java.util.regex.Pattern;
-
-/**
- * Used to parse cron expressions.
- * <p>The pattern comprises a list of six single space-separated fields: second,
- * minute, hour, day, month, weekday. </p>
- * <p>Month and weekday names can be given as the first three letters of the
- * English names.</p>
- * <br>
- * Examples:
- * <ul>
- * <li>"0 0 * * * *" = the top of every hour of every day.</li>
- * <li>"*/10 * * * * *" = every ten seconds.</li>
- * <li>"0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day.</li>
- * <li>"0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30 and 10 o'clock every
- * day.</li>
- * <li>"0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays</li>
- * <li>"0 0 0 25 12 ?" = every Christmas Day at midnight</li>
- * <li>"@daily" = every day</li>
- * <li>"@annually" = every year</li>
- * <li>"@weekly" = every week</li>
- * <li>"@hourly" = every hour</li>
- * <li>"@monthly" = every month</li>
- * </ul>
- */
-public final class EmbeddedCronParser {
-
- private final String expression;
- private final TimeZone timeZone;
-
- private int yearMin;
- private int yearMax;
-
- private final BitSet months = new BitSet(12);
- private final BitSet daysOfMonth = new BitSet(31);
- private final BitSet daysOfWeek = new BitSet(7);
- private final BitSet hours = new BitSet(24);
- private final BitSet minutes = new BitSet(60);
- private final BitSet seconds = new BitSet(60);
-
- private static final Pattern SPACE_SPLITTER = Pattern.compile("[ ]+");
-
- /**
- * Construct a {@link EmbeddedCronParser} from the pattern provided, using the
- * default {@link TimeZone}.
- *
- * @param expression a space-separated list of time fields
- * @throws IllegalArgumentException if the pattern cannot be parsed
- * @see java.util.TimeZone#getDefault()
- */
- public EmbeddedCronParser(final String expression) {
- this(expression, TimeZone.getDefault());
- }
-
- /**
- * Construct a {@link EmbeddedCronParser} from the pattern provided, using the
- * specified {@link TimeZone}.
- *
- * @param expression a space-separated list of time fields
- * @param timeZone the TimeZone to use for generated trigger times
- * @throws IllegalArgumentException if the pattern cannot be parsed
- */
- public EmbeddedCronParser(final String expression, final TimeZone timeZone) {
- if (expression.startsWith("@")) {
- this.expression = preDeclared(expression);
- } else {
- this.expression = expression;
- }
- this.timeZone = timeZone;
- parse(this.expression);
- }
-
- /**
- * Get the next {@link Date} in the sequence matching the Cron pattern and after
- * the value provided. The return value will have a whole number of seconds, and
- * will be after the input value.
- *
- * @param date a seed value
- * @return the next value matching the pattern
- */
- public long next(final long date) {
- /*
- * The plan: 1 Round up to the next whole second 2 If seconds match move on,
- * otherwise find the next match: 2.1 If next match is in the next minute then
- * roll forwards 3 If minute matches move on, otherwise find the next match 3.1
- * If next match is in the next hour then roll forwards 3.2 Reset the seconds
- * and go to 2 4 If hour matches move on, otherwise find the next match 4.1 If
- * next match is in the next day then roll forwards, 4.2 Reset the minutes and
- * seconds and go to 2 ...
- */
- final Calendar calendar = new GregorianCalendar();
- calendar.setTimeZone(timeZone);
- calendar.setTimeInMillis(date);
-
- // First, just reset the milliseconds and try to calculate from there...
- calendar.set(Calendar.MILLISECOND, 0);
- final long originalTimestamp = calendar.getTimeInMillis();
- int currentYear = calendar.get(Calendar.YEAR);
- doNext(calendar, currentYear);
-
- if (calendar.getTimeInMillis() == originalTimestamp) {
- // We arrived at the original timestamp - round up to the next whole second and
- // try again...
- calendar.add(Calendar.SECOND, 1);
- doNext(calendar, currentYear);
- }
-
- return calendar.getTimeInMillis();
- }
-
- private void doNext(final Calendar calendar, final int yearOfInputDate) {
- final List<Integer> resets = new ArrayList<>();
-
- final int second = calendar.get(Calendar.SECOND);
- final int updateSecond = findNext(seconds, second, calendar, Calendar.SECOND, Calendar.MINUTE, Collections.emptyList());
- if (second == updateSecond) {
- resets.add(Calendar.SECOND);
- }
-
- final int minute = calendar.get(Calendar.MINUTE);
- final int updateMinute = findNext(minutes, minute, calendar, Calendar.MINUTE, Calendar.HOUR_OF_DAY, resets);
- if (minute == updateMinute) {
- resets.add(Calendar.MINUTE);
- } else {
- doNext(calendar, yearOfInputDate);
- }
-
- final int hour = calendar.get(Calendar.HOUR_OF_DAY);
- final int updateHour = findNext(hours, hour, calendar, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_WEEK, resets);
- if (hour == updateHour) {
- resets.add(Calendar.HOUR_OF_DAY);
- } else {
- doNext(calendar, yearOfInputDate);
- }
-
- final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
- final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
- final int updateDayOfMonth = findNextDay(calendar, daysOfMonth, dayOfMonth, daysOfWeek, dayOfWeek, resets);
- if (dayOfMonth == updateDayOfMonth) {
- resets.add(Calendar.DAY_OF_MONTH);
- } else {
- doNext(calendar, yearOfInputDate);
- }
-
- final int month = calendar.get(Calendar.MONTH);
- final int updateMonth = findNext(months, month, calendar, Calendar.MONTH, Calendar.YEAR, resets);
- if (month == updateMonth) {
- resets.add(Calendar.MONTH);
- } else {
- if (calendar.get(Calendar.YEAR) - yearOfInputDate > 4) {
- throw new IllegalArgumentException(
- "Invalid cron expression \"" + expression + "\" led to runaway search for next trigger");
- }
- doNext(calendar, yearOfInputDate);
- }
-
- final int year = calendar.get(Calendar.YEAR);
- final int updateYear = findNextYear(yearMin, yearMax, year, calendar, yearOfInputDate, resets);
- if (year == updateYear) {
- resets.add(Calendar.YEAR);
- } else {
- doNext(calendar, yearOfInputDate);
- }
- }
-
- private int findNextYear(int yearMin, int yearMax, int year, Calendar calendar, int yearOfInputDate, List<Integer> lowerOrders) {
-
- int nextYearVal = year;
- if(year < yearMin) {
- nextYearVal = yearMin;
- } else if(year > yearMax) {
- nextYearVal = calendar.getMaximum(Calendar.YEAR);
- }
- if(nextYearVal != year) {
- calendar.set(Calendar.YEAR, nextYearVal);
- reset(calendar, lowerOrders);
- }
- return nextYearVal;
- }
-
- private int findNextDay(final Calendar calendar, final BitSet daysOfMonth, int dayOfMonth, final BitSet daysOfWeek,
- int dayOfWeek, final List<Integer> resets) {
- int count = 0;
- final int max = 366;
- // the DAY_OF_WEEK values in java.util.Calendar start with 1 (Sunday),
- // but in the cron pattern, they start with 0, so we subtract 1 here
- while ((!daysOfMonth.get(dayOfMonth) || !daysOfWeek.get(dayOfWeek - 1)) && count++ < max) {
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
- dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
- reset(calendar, resets);
- }
- if (count >= max) {
- throw new IllegalArgumentException("Overflow in day for expression \"" + expression + "\"");
- }
- return dayOfMonth;
- }
-
- /**
- * Search the bits provided for the next set bit after the value provided, and
- * reset the calendar.
- *
- * @param bits a {@link BitSet} representing the allowed values of the
- * field
- * @param value the current value of the field
- * @param calendar the calendar to increment as we move through the bits
- * @param field the field to increment in the calendar (@see
- * {@link Calendar} for the static constants defining valid
- * fields)
- * @param lowerOrders the Calendar field ids that should be reset (i.e. the ones
- * of lower significance than the field of interest)
- * @return the value of the calendar field that is next in the sequence
- */
- private int findNext(final BitSet bits, final int value, final Calendar calendar, final int field,
- final int nextField, final List<Integer> lowerOrders) {
- int nextValue = bits.nextSetBit(value);
- // roll over if needed
- if (nextValue == -1) {
- calendar.add(nextField, 1);
- reset(calendar, Collections.singletonList(field));
- nextValue = bits.nextSetBit(0);
- }
- if (nextValue != value) {
- calendar.set(field, nextValue);
- reset(calendar, lowerOrders);
- }
- return nextValue;
- }
-
- /**
- * Reset the calendar setting all the fields provided to zero.
- */
- private void reset(final Calendar calendar, final List<Integer> fields) {
- for (final int field : fields) {
- calendar.set(field, field == Calendar.DAY_OF_MONTH ? 1 : 0);
- }
- }
-
- // Parsing logic invoked by the constructor
-
- /**
- * Parse the given pattern expression.
- */
- private void parse(final String expression) {
- String[] fields = SPACE_SPLITTER.split(expression);
-
- setYears(expression, fields);
- setNumberHits(seconds, fields[0], 0, 60);
- setNumberHits(minutes, fields[1], 0, 60);
- setNumberHits(hours, fields[2], 0, 24);
- setDaysOfMonth(daysOfMonth, fields[3]);
- setMonths(months, fields[4]);
- setDays(daysOfWeek, replaceOrdinals(fields[5], "SUN,MON,TUE,WED,THU,FRI,SAT"), 8);
- if (daysOfWeek.get(7)) {
- // Sunday can be represented as 0 or 7
- daysOfWeek.set(0);
- daysOfWeek.clear(7);
- }
- }
-
- private void setYears(final String expression, String[] fields) {
- if (fields.length == 7) {
- String yearField = fields[6];
- if("*".equals(yearField)) {
- yearMin = 0;
- yearMax = Integer.MAX_VALUE;
- } else {
- String[] yearParts = yearField.split("-");
- if(yearParts.length == 1) {
- yearMin = yearMax = Integer.parseInt(yearParts[0]);
- } else if(yearParts.length == 2) {
- yearMin = Integer.parseInt(yearParts[0]);
- yearMax = Integer.parseInt(yearParts[1]);
- } else {
- throw new IllegalArgumentException(String.format("Invalid year field '%s' in expression '%s'", yearField, expression)) ;
- }
-
- }
- } else if (fields.length == 6) {
- yearMin = 0;
- yearMax = Integer.MAX_VALUE;
- } else {
- throw new IllegalArgumentException(String.format(
- "Cron expression must consist of 6 or 7 fields (found %d in \"%s\")", fields.length, expression));
- }
- }
-
- /**
- * Replace the values in the comma-separated list (case insensitive) with their
- * index in the list.
- *
- * @return a new String with the values from the list replaced
- */
- private String replaceOrdinals(String value, final String commaSeparatedList) {
- final String[] list = commaSeparatedList.split(",");
- for (int i = 0; i < list.length; i++) {
- final String item = list[i].toUpperCase();
- value = value.toUpperCase().replace(item, "" + i);
- }
- return value;
- }
-
- private void setDaysOfMonth(final BitSet bits, final String field) {
- final int max = 31;
- // Days of month start with 1 (in Cron and Calendar) so add one
- setDays(bits, field, max + 1);
- // ... and remove it from the front
- bits.clear(0);
- }
-
- private void setDays(final BitSet bits, String field, final int max) {
- if (field.contains("?")) {
- field = "*";
- }
- setNumberHits(bits, field, 0, max);
- }
-
- private void setMonths(final BitSet bits, String value) {
- final int max = 12;
- value = replaceOrdinals(value, "FOO,JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC");
- final BitSet months = new BitSet(13);
- // Months start with 1 in Cron and 0 in Calendar, so push the values first into
- // a longer bit set
- setNumberHits(months, value, 1, max + 1);
- // ... and then rotate it to the front of the months
- for (int i = 1; i <= max; i++) {
- if (months.get(i)) {
- bits.set(i - 1);
- }
- }
- }
-
- private void setNumberHits(final BitSet bits, final String value, final int min, final int max) {
- final String[] fields = value.split(",");
- for (final String field : fields) {
- if (!field.contains("/")) {
- // Not an incrementer so it must be a range (possibly empty)
- final int[] range = getRange(field, min, max);
- bits.set(range[0], range[1] + 1);
- } else {
- final String[] split = field.split("/");
- if (split.length > 2) {
- throw new IllegalArgumentException("Incrementer has more than two fields: '" + field
- + "' in expression \"" + expression + "\"");
- }
- final int[] range = getRange(split[0], min, max);
- if (!split[0].contains("-")) {
- range[1] = max - 1;
- }
- final int delta = Integer.parseInt(split[1]);
- if (delta <= 0) {
- throw new IllegalArgumentException("Incrementer delta must be 1 or higher: '" + field
- + "' in expression \"" + expression + "\"");
- }
- for (int i = range[0]; i <= range[1]; i += delta) {
- bits.set(i);
- }
- }
- }
- }
-
- private int[] getRange(final String field, final int min, final int max) {
- final int[] result = new int[2];
- if (field.contains("*")) {
- result[0] = min;
- result[1] = max - 1;
- return result;
- }
- if (!field.contains("-")) {
- result[0] = result[1] = Integer.valueOf(field);
- } else {
- final String[] split = field.split("-");
- if (split.length > 2) {
- throw new IllegalArgumentException(
- "Range has more than two fields: '" + field + "' in expression \"" + expression + "\"");
- }
- result[0] = Integer.valueOf(split[0]);
- result[1] = Integer.valueOf(split[1]);
- }
- if (result[0] >= max || result[1] >= max) {
- throw new IllegalArgumentException(
- "Range exceeds maximum (" + max + "): '" + field + "' in expression \"" + expression + "\"");
- }
- if (result[0] < min || result[1] < min) {
- throw new IllegalArgumentException(
- "Range less than minimum (" + min + "): '" + field + "' in expression \"" + expression + "\"");
- }
- return result;
- }
-
- /**
- * Convert predeclared words into their representing cron expression.
- *
- * <pre>
- * @yearly (or @annually) Run once a year at midnight on the morning of January 1 0 0 0 1 1 *
- * @monthly Run once a month at midnight on the morning of the first day of the month 0 0 0 1 * *
- * @weekly Run once a week at midnight on Sunday morning 0 0 0 * * 0
- * @daily Run once a day at midnight 0 0 0 * * *
- * @hourly Run once an hour at the beginning of the hour 0 0 * * * *
- * @reboot Run at startup 0 0 0 1 1 ? 1900
- * </pre>
- */
- private String preDeclared(final String expression) {
- switch (expression) {
- case "@annually":
- case "@yearly":
- return "0 0 0 1 1 *";
- case "@monthly":
- return "0 0 0 1 * *";
- case "@weekly":
- return "0 0 0 ? * MON";
- case "@daily":
- return "0 0 0 * * ?";
- case "@hourly":
- return "0 0 * * * ?";
- default:
- throw new IllegalArgumentException("Unrecognized @ expression: '" + expression + "'");
- }
- }
-
- @Override
- public boolean equals(final Object other) {
- if (this == other) {
- return true;
- }
- if (!(other instanceof EmbeddedCronParser)) {
- return false;
- }
- final EmbeddedCronParser otherCron = (EmbeddedCronParser) other;
- return months.equals(otherCron.months) && daysOfMonth.equals(otherCron.daysOfMonth)
- && daysOfWeek.equals(otherCron.daysOfWeek) && hours.equals(otherCron.hours)
- && minutes.equals(otherCron.minutes) && seconds.equals(otherCron.seconds);
- }
-
- @Override
- public int hashCode() {
- return 17 * months.hashCode() + 29 * daysOfMonth.hashCode() + 37 * daysOfWeek.hashCode() + 41 * hours.hashCode()
- + 53 * minutes.hashCode() + 61 * seconds.hashCode();
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + ": " + expression;
- }
-
- String getExpression() {
- return expression;
- }
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronScheduler.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronScheduler.java
deleted file mode 100644
index 7c72a11bd7..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronScheduler.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
-
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public final class EmbeddedCronScheduler implements Runnable {
-
- private static final Logger LOG = LoggerFactory.getLogger(EmbeddedCronScheduler.class);
-
- /* List of managed tasks */
- private final Map<String, AsyncEmbeddedCronJob> tasks = new ConcurrentHashMap<>();
-
- /* Task executor service */
- public final ScheduledExecutorService scheduledExecutorService;
-
- private String schedulerThreadName;
-
- public EmbeddedCronScheduler(final ScheduledExecutorService scheduledExecutorService, final long initialDelay, final long checkInterval,
- final TimeUnit timeUnit, final String schedulerThreadName) {
-
- this.scheduledExecutorService = scheduledExecutorService;
- scheduledExecutorService.scheduleAtFixedRate(this, initialDelay, checkInterval, timeUnit);
-
- this.schedulerThreadName = schedulerThreadName;
- }
-
- public void schedule(AsyncEmbeddedCronJob cronJob) {
- tasks.put(cronJob.getId(), cronJob);
- }
-
- public void remove(AsyncEmbeddedCronJob cronJob) {
- tasks.remove(cronJob.getId());
- }
-
- @Override
- public void run() {
- String threadNameToRestore = Thread.currentThread().getName();
- try {
- Thread.currentThread().setName(schedulerThreadName);
- LOG.trace("tasks: {}", tasks);
-
- for (final Entry<String, AsyncEmbeddedCronJob> entry : tasks.entrySet()) {
- final AsyncEmbeddedCronJob job = entry.getValue();
- job.checkAndExecute(scheduledExecutorService);
- }
- } finally {
- Thread.currentThread().setName(threadNameToRestore);
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerProvider.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerProvider.java
deleted file mode 100644
index 884677a143..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronSchedulerProvider.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
-
-import static java.util.concurrent.TimeUnit.SECONDS;
-
-import java.util.concurrent.TimeUnit;
-
-import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronSchedulerProvider.Configuration;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@Designate(ocd = Configuration.class)
-@Component(service = EmbeddedCronSchedulerProvider.class)
-public class EmbeddedCronSchedulerProvider {
-
- public static final String HC_SCHEDULER_NAME = "felix.hc.embedded.scheduler";
-
- @ObjectClassDefinition(name = "Health Check: Embedded Cron Scheduler")
- @interface Configuration {
- @AttributeDefinition(name = "Initial Delay", description = "The initial delay before tasks are scheduled")
- long initialDelay() default 3L;
-
- @AttributeDefinition(name = "Check Interval Delay", description = "The delay to check for a scheduled task to satisfy the cron expression")
- long checkInterval() default 1L;
-
- @AttributeDefinition(name = "Time unit", description = "The time unit of the delays as specified in this configuration")
- TimeUnit timeUnit() default SECONDS;
-
- @AttributeDefinition(name = "Scheduler Name", description = "The embedded cron scheduler thread name")
- String schedulerName() default HC_SCHEDULER_NAME;
- }
-
-
- @Reference
- private HealthCheckExecutorThreadPool threadPool;
-
- private Configuration configuration;
-
- private EmbeddedCronScheduler scheduler = null;
-
- @Activate
- void activate(final Configuration configuration) {
- this.configuration = configuration;
- }
-
- public synchronized EmbeddedCronScheduler getScheduler() {
- if(scheduler == null) {
- scheduler = new EmbeddedCronScheduler(
- threadPool.getExecutor(),
- configuration.initialDelay(),
- configuration.checkInterval(),
- configuration.timeUnit(),
- configuration.schedulerName());
- }
- return scheduler;
-
- }
-
-
- // @Deactivate
- // no shutdown of scheduler needed since HealthCheckExecutorThreadPool is handling the shutdown
- // of the full ScheduledThreadPoolExecutor
-
-
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/AsyncQuartzCronJob.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/AsyncQuartzCronJob.java
deleted file mode 100644
index 67046cb47e..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/AsyncQuartzCronJob.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling.cron.quartz;
-
-import static org.quartz.CronScheduleBuilder.cronSchedule;
-import static org.quartz.JobBuilder.newJob;
-import static org.quartz.TriggerBuilder.newTrigger;
-
-import org.apache.felix.hc.core.impl.scheduling.AsyncJob;
-import org.quartz.CronTrigger;
-import org.quartz.Job;
-import org.quartz.JobDataMap;
-import org.quartz.JobDetail;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-import org.quartz.JobKey;
-import org.quartz.Scheduler;
-import org.quartz.SchedulerException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Async job to be used by async health checks and async monitor.
- *
- * This implementation uses quartz to support the cron syntax (which is not supported by executors from standard java java.util.concurrent
- * package) */
-public class AsyncQuartzCronJob extends AsyncJob {
- private static final Logger LOG = LoggerFactory.getLogger(AsyncQuartzCronJob.class);
-
- private static final String JOB_DATA_KEY_JOB = "asyncHcJob";
-
- private final String id;
- private final String group;
- private final String cronExpression;
-
- private final Scheduler quartzScheduler;
-
- private JobKey jobKey = null;
-
- public AsyncQuartzCronJob(Runnable runnable, QuartzCronSchedulerProvider quartzCronSchedulerProvider, String id, String group, String cronExpression) {
- super(runnable);
- this.quartzScheduler = quartzCronSchedulerProvider.getQuartzCronScheduler().getScheduler();
- this.id = id;
- this.group = group;
- this.cronExpression = cronExpression;
- }
-
- public JobKey getJobKey() {
- return jobKey;
- }
-
- private JobDetail getQuartzJobDetail() {
- JobDataMap jobData = new JobDataMap();
- jobData.put(JOB_DATA_KEY_JOB, runnable);
-
- JobDetail job = newJob(AsyncQuartzCronJob.QuartzJob.class).setJobData(jobData)
- .withIdentity(id, group)
- .build();
-
- jobKey = job.getKey();
-
- return job;
- }
-
- public boolean schedule() {
-
- try {
-
- JobDetail job = getQuartzJobDetail();
- CronTrigger cronTrigger = newTrigger().withSchedule(cronSchedule(cronExpression)).forJob(job)
- .build();
-
- quartzScheduler.scheduleJob(job, cronTrigger);
- LOG.info("Scheduled job {} with trigger {}", job, cronTrigger);
- return true;
- } catch (SchedulerException e) {
- LOG.error("Could not schedule job for " + runnable + ": " + e, e);
- return false;
- }
-
- }
-
- @Override
- public boolean unschedule() {
- LOG.debug("Unscheduling job {}", jobKey);
- try {
- quartzScheduler.deleteJob(jobKey);
- return true;
- } catch (SchedulerException e) {
- LOG.error("Could not unschedule job for " + jobKey + ": " + e, e);
- return false;
- }
- }
-
- @Override
- public String toString() {
- return "[Async quartz job for " + runnable + "]";
- }
-
- // quartz forces to pass in a class object (and not an instance), hence this helper class is needed
- public static class QuartzJob implements Job {
-
- @Override
- public void execute(JobExecutionContext context) throws JobExecutionException {
- Runnable ayncJob = (Runnable) context.getJobDetail().getJobDataMap().get(JOB_DATA_KEY_JOB);
- ayncJob.run();
- }
- }
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronScheduler.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronScheduler.java
deleted file mode 100644
index 93ff9e82f6..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronScheduler.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling.cron.quartz;
-
-import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.quartz.Scheduler;
-import org.quartz.SchedulerException;
-import org.quartz.impl.DirectSchedulerFactory;
-import org.quartz.simpl.RAMJobStore;
-import org.quartz.spi.ThreadPool;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Wrapper around org.quartz.Scheduler */
-public class QuartzCronScheduler {
- private static final Logger LOG = LoggerFactory.getLogger(QuartzCronScheduler.class);
-
- private static final String HC_SCHEDULER_NAME = "quartz.hc.scheduler_name";
-
- private Scheduler scheduler;
-
- public QuartzCronScheduler(HealthCheckExecutorThreadPool healthCheckExecutorThreadPool) {
- try {
- DirectSchedulerFactory schedulerFactory = DirectSchedulerFactory.getInstance();
- ThreadPool threadPool = new QuartzThreadPool(healthCheckExecutorThreadPool);
- schedulerFactory.createScheduler(HC_SCHEDULER_NAME, "id_" + System.currentTimeMillis(), threadPool, new RAMJobStore());
- scheduler = schedulerFactory.getScheduler(HC_SCHEDULER_NAME);
- scheduler.start();
- LOG.debug("Started quartz scheduler {}", scheduler);
- } catch (SchedulerException e) {
- throw new IllegalStateException("Could not initialise/start quartz scheduler " + HC_SCHEDULER_NAME, e);
- }
- }
-
- public void shutdown() {
- if (scheduler != null) {
- try {
- scheduler.shutdown(false);
- LOG.debug("Shutdown of quartz scheduler finished: {}", scheduler);
- } catch (SchedulerException e) {
- throw new IllegalStateException("Could not shutdown quartz scheduler " + HC_SCHEDULER_NAME, e);
- }
- }
- }
-
- public Scheduler getScheduler() {
- return scheduler;
- }
-
- public class QuartzThreadPool implements ThreadPool {
-
- private final HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
-
- public QuartzThreadPool(HealthCheckExecutorThreadPool healthCheckExecutorThreadPool) {
- this.healthCheckExecutorThreadPool = healthCheckExecutorThreadPool;
- }
-
- /** see org.quartz.spi.QuartzThreadPool#getPoolSize() */
- @Override
- public int getPoolSize() {
- return healthCheckExecutorThreadPool.getPoolSize();
- }
-
- /** see org.quartz.spi.QuartzThreadPool#initialize() */
- @Override
- public void initialize() {
- // nothing to do
- }
-
- /** see org.quartz.spi.ThreadPool#setInstanceId(java.lang.String) */
- @Override
- public void setInstanceId(final String id) {
- // we ignore this
- }
-
- /** see org.quartz.spi.ThreadPool#setInstanceName(java.lang.String) */
- @Override
- public void setInstanceName(final String name) {
- // we ignore this
- }
-
- /** see org.quartz.spi.QuartzThreadPool#runInThread(java.lang.Runnable) */
- @Override
- public boolean runInThread(final Runnable job) {
- healthCheckExecutorThreadPool.execute(job);
- return true;
- }
-
- /** see org.quartz.spi.ThreadPool#blockForAvailableThreads() */
- @Override
- public int blockForAvailableThreads() {
- return healthCheckExecutorThreadPool.getMaxCurrentlyAvailableThreads();
- }
-
- /** see org.quartz.spi.QuartzThreadPool#shutdown(boolean) */
- @Override
- public void shutdown(final boolean waitForJobsToComplete) {
- // this executor is bound to the SCR lifecycle of HealthCheckExecutorThreadPool
- }
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronSchedulerProvider.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronSchedulerProvider.java
deleted file mode 100644
index ef9c545146..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/scheduling/cron/quartz/QuartzCronSchedulerProvider.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling.cron.quartz;
-
-import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Component without direct quartz imports (can always start) that will provide a QuartzCronScheduler on demand. */
-@Component(service = QuartzCronSchedulerProvider.class)
-public class QuartzCronSchedulerProvider {
-
- private static final Logger LOG = LoggerFactory.getLogger(QuartzCronSchedulerProvider.class);
-
- private QuartzCronScheduler quartzCronScheduler = null;
-
- @Reference
- HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
-
- public synchronized QuartzCronScheduler getQuartzCronScheduler() {
- if (quartzCronScheduler == null) {
- quartzCronScheduler = new QuartzCronScheduler(healthCheckExecutorThreadPool);
- LOG.info("Created QuartzCronScheduler");
- }
- return quartzCronScheduler;
- }
-
- @Deactivate
- protected synchronized void deactivate() {
- if (quartzCronScheduler != null) {
- quartzCronScheduler.shutdown();
- quartzCronScheduler = null;
- LOG.info("Shutdown of QuartzCronScheduler");
- }
- }
-
-}
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
deleted file mode 100644
index 0b18fe6f54..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServlet.java
+++ /dev/null
@@ -1,501 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.servlet;
-
-import static org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME;
-import static org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT;
-import static org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN;
-import static org.osgi.service.servlet.whiteboard.HttpWhiteboardConstants.HTTP_WHITEBOARD_TARGET;
-
-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 org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-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.metatype.annotations.Designate;
-import org.osgi.service.servlet.whiteboard.annotations.RequireHttpWhiteboard;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import jakarta.servlet.Servlet;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServlet;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-
-/** Servlet that triggers the health check executor to return results via http.
- *
- * Parameters:
- * <ul>
- * <li>tags: The health check tags to take into account
- * <li>format: html|json|jsonp|txt|verbose.txt
- * <li>includeDebug: If true, debug messages from result log are included.
- * <li>callback: For jsonp, the JS callback function name (defaults to "processHealthCheckResults")
- * <li>httpStatus: health check status to http status mapping in format httpStatus=WARN:418,CRITICAL:503,HEALTH_CHECK_ERROR:500.
- * </ul>
- *
- * For omitted health check status values the next best code will be used (e.g. for httpStatus=CRITICAL:503 a result WARN will return 200,
- * CRITICAL 503 and HEALTH_CHECK_ERROR also 503). By default all requests answer with an http status of 200.
- * <p>
- * Useful in combination with load balancers. */
-@RequireHttpWhiteboard
-@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
-@Designate(ocd = HealthCheckExecutorServletConfiguration.class, factory=true)
-public class HealthCheckExecutorServlet extends HttpServlet {
- private static final long serialVersionUID = 8013511523994541848L;
-
- private final Logger LOG = LoggerFactory.getLogger(this.getClass());
-
- public static final String PARAM_SPLIT_REGEX = "[,;]+";
-
- static class Param {
- final String name;
- String description;
-
- Param(String n, String d) {
- name = n;
- description = d;
- }
-
- void setDescription(String d) {
- description = d;
- }
- }
-
- static final Param PARAM_TAGS = new Param("tags",
- "Comma-separated list of health checks tags to select - can also be specified via path, e.g. /system/health/tag1,tag2.json. Exclusions can be done by prepending '-' to the tag name");
- static final Param PARAM_FORMAT = new Param("format", null /* to be set in activate() */);
- static final Param PARAM_HTTP_STATUS = new Param("httpStatus", "Specify HTTP result code, for example"
- + " CRITICAL:503 (status 503 if result >= CRITICAL)"
- + " or CRITICAL:503,HEALTH_CHECK_ERROR:500,OK:418 for more specific HTTP status");
-
- static final Param PARAM_COMBINE_TAGS_WITH_OR = new Param("combineTagsWithOr",
- "Combine tags with OR, active by default. Set to false to combine with AND");
- static final Param PARAM_FORCE_INSTANT_EXECUTION = new Param("forceInstantExecution",
- "If true, forces instant execution by executing async health checks directly, circumventing the cache (2sec by default) of the HealthCheckExecutor");
- static final Param PARAM_OVERRIDE_GLOBAL_TIMEOUT = new Param("timeout",
- "(msec) a timeout status is returned for any health check still running after this period. Overrides the default HealthCheckExecutor timeout");
-
- static final Param PARAM_INCLUDE_DEBUG = new Param("hcDebug", "Include the DEBUG output of the Health Checks");
-
- static final Param PARAM_NAMES = new Param("names",
- "Comma-separated list of health check names to select. Exclusions can be done by prepending '-' to the health check name");
-
- static final String JSONP_CALLBACK_DEFAULT = "processHealthCheckResults";
- static final Param PARAM_JSONP_CALLBACK = new Param("callback",
- "name of the JSONP callback function to use, defaults to " + JSONP_CALLBACK_DEFAULT);
-
- static final Param[] PARAM_LIST = { PARAM_TAGS, PARAM_NAMES, PARAM_FORMAT, PARAM_HTTP_STATUS, PARAM_COMBINE_TAGS_WITH_OR,
- PARAM_FORCE_INSTANT_EXECUTION, PARAM_OVERRIDE_GLOBAL_TIMEOUT, PARAM_INCLUDE_DEBUG, PARAM_JSONP_CALLBACK };
-
- static final String FORMAT_HTML = "html";
- static final String FORMAT_JSON = "json";
- static final String FORMAT_JSONP = "jsonp";
- static final String FORMAT_TXT = "txt";
- static final String FORMAT_VERBOSE_TXT = "verbose.txt";
-
- private static final String CONTENT_TYPE_HTML = "text/html";
- private static final String CONTENT_TYPE_TXT = "text/plain";
- private static final String CONTENT_TYPE_JSON = "application/json";
- private static final String CONTENT_TYPE_JSONP = "application/javascript";
- private static final String STATUS_HEADER_NAME = "X-Health";
-
- 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 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
- HealthCheckExecutor healthCheckExecutor;
-
- @Reference
- ResultHtmlSerializer htmlSerializer;
-
- @Reference
- ResultJsonSerializer jsonSerializer;
-
- @Reference
- ResultTxtSerializer txtSerializer;
-
- @Reference
- ResultTxtVerboseSerializer verboseTxtSerializer;
-
- @Activate
- 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();
- this.servletDefaultTags = configuration.tags();
- this.defaultCombineTagsWithOr = configuration.combineTagsWithOr();
- this.defaultFormat = configuration.format();
- this.allowedFormats = configuration.allowed_formats();
- // make sure to include default format
- if ( !this.isFormatAllowed(this.defaultFormat) ) {
- final String[] allFormats = new String[this.allowedFormats.length + 1];
- System.arraycopy(this.allowedFormats, 0, allFormats, 0, this.allowedFormats.length);
- allFormats[this.allowedFormats.length] = this.defaultFormat;
- this.allowedFormats = allFormats;
- }
- PARAM_FORMAT.setDescription("Output format, " + String.join("|", allowedFormats) + " - an extension in the URL overrides this");
-
- this.corsAccessControlAllowOrigin = configuration.cors_accessControlAllowOrigin();
- this.disableRequestConfiguration = configuration.disable_request_configuration();
-
- if ( configuration.disabled() ) {
- LOG.info("Health Check Servlet is disabled by configuration");
- return;
- }
-
- LOG.info("Health Check Servlet Configuration: servletPath={}, defaultStatusMapping={}, servletDefaultTimeout={}, " +
- "servletDefaultTags={}, defaultCombineTagsWithOr={}, defaultFormat={}, allowedFormats={}, corsAccessControlAllowOrigin={}",
- 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) ) {
- servletsToRegister.put(this.servletPath.concat(".").concat(FORMAT_HTML), new ProxyServlet(FORMAT_HTML));
- }
- if ( isFormatAllowed(FORMAT_JSON) ) {
- servletsToRegister.put(this.servletPath.concat(".").concat(FORMAT_JSON), new ProxyServlet(FORMAT_JSON));
- }
- if ( isFormatAllowed(FORMAT_JSONP) ) {
- servletsToRegister.put(this.servletPath.concat(".").concat(FORMAT_JSONP), new ProxyServlet(FORMAT_JSONP));
- }
- if ( isFormatAllowed(FORMAT_TXT) ) {
- servletsToRegister.put(this.servletPath.concat(".").concat(FORMAT_TXT), new ProxyServlet(FORMAT_TXT));
- }
- if ( isFormatAllowed(FORMAT_VERBOSE_TXT) ) {
- servletsToRegister.put(this.servletPath.concat(".").concat(FORMAT_VERBOSE_TXT), new ProxyServlet(FORMAT_VERBOSE_TXT));
- }
-
- for (final Map.Entry<String, HttpServlet> servlet : servletsToRegister.entrySet()) {
- try {
- LOG.info("Registering HC Servlet > Name: '{}', Path: '{}'", getClass().getSimpleName(),
- servlet.getKey());
- ServletInfoDTO servletInfo = new ServletInfoDTO(configuration.servletContextName(), configuration.osgi_http_whiteboard_target(),
- servlet.getKey(), servlet.getValue());
- registerServlet(servletInfo);
- } catch (Exception e) {
- LOG.error("Could not register health check servlet: " + e, e);
- }
- }
- }
-
- @Deactivate
- public void deactivate() {
- for (final Entry<String, ServiceRegistration<Servlet>> entry : servletRegistrations.entrySet()) {
- try {
- LOG.info("Unregistering HC Servlet {} from path {}", getClass().getSimpleName(), entry.getKey());
- entry.getValue().unregister();
- } catch (Exception e) {
- // ignore the exception - this might happen on shutdown
- }
- }
- servletRegistrations.clear();
- }
-
- private void registerServlet(final ServletInfoDTO servletInfo) {
- final Dictionary<String, Object> properties = new Hashtable<>();
-
- if (servletInfo.whiteboardTarget != null && !servletInfo.whiteboardTarget.isEmpty()) {
- properties.put(HTTP_WHITEBOARD_TARGET, servletInfo.whiteboardTarget);
- }
- if (servletInfo.contextName != null && !servletInfo.contextName.isEmpty()) {
- properties.put(HTTP_WHITEBOARD_CONTEXT_SELECT, "(".concat(HTTP_WHITEBOARD_CONTEXT_NAME).concat("=").concat(servletInfo.contextName).concat(")"));
- }
- properties.put(HTTP_WHITEBOARD_SERVLET_PATTERN, servletInfo.servletPath);
-
- final ServiceRegistration<Servlet> registration = bundleContext.registerService(Servlet.class, servletInfo.servlet, properties);
- servletRegistrations.put(servletPath, registration);
- }
-
- /**
- * Check if the format is allowed
- * @param format The format
- * @return {@code true} if allowed
- */
- private boolean isFormatAllowed(final String format) {
- for(final String f : this.allowedFormats) {
- if ( f.equals(format) ) {
- return true;
- }
- }
- return false;
- }
-
- protected void doGet(final HttpServletRequest request, final HttpServletResponse response, final String pathTokensStr, final String format)
- throws ServletException, IOException {
- HealthCheckSelector selector = HealthCheckSelector.empty();
-
- List<String> tags = new ArrayList<String>();
- List<String> names = new ArrayList<String>();
-
- if (!disableRequestConfiguration && StringUtils.isNotBlank(pathTokensStr)) {
- String[] pathTokens = pathTokensStr.split(PARAM_SPLIT_REGEX);
- for (String pathToken : pathTokens) {
- if (pathToken.indexOf(' ') >= 0) {
- // token contains space. assume it is a name
- names.add(pathToken);
- } else {
- tags.add(pathToken);
- }
- }
- }
- if (tags.size() == 0) {
- // if not provided via path use parameter or configured default
- String tagsParameter = this.disableRequestConfiguration ? null : request.getParameter(PARAM_TAGS.name);
- tags = Arrays.asList(StringUtils.isNotBlank(tagsParameter) ? tagsParameter.split(PARAM_SPLIT_REGEX): servletDefaultTags);
- }
- selector.withTags(tags.toArray(new String[0]));
-
- if (names.size() == 0 && !this.disableRequestConfiguration) {
- // if not provided via path use parameter or default
- names = Arrays.asList(StringUtils.defaultIfBlank(request.getParameter(PARAM_NAMES.name), "").split(PARAM_SPLIT_REGEX));
- }
- selector.withNames(names.toArray(new String[0]));
-
- final boolean includeDebug = this.disableRequestConfiguration ? false : Boolean.valueOf(request.getParameter(PARAM_INCLUDE_DEBUG.name));
-
- String httpStatusMappingParameterVal = this.disableRequestConfiguration ? null : request.getParameter(PARAM_HTTP_STATUS.name);
- final Map<Result.Status, Integer> statusMapping = httpStatusMappingParameterVal!=null ? getStatusMapping(httpStatusMappingParameterVal) : defaultStatusMapping;
-
- HealthCheckExecutionOptions executionOptions = new HealthCheckExecutionOptions();
-
- String paramCombineTagsWithOr = this.disableRequestConfiguration ? null : request.getParameter(PARAM_COMBINE_TAGS_WITH_OR.name);
- executionOptions.setCombineTagsWithOr( paramCombineTagsWithOr!=null ? Boolean.valueOf(paramCombineTagsWithOr) : defaultCombineTagsWithOr);
-
- if ( !this.disableRequestConfiguration ) {
- executionOptions.setForceInstantExecution(Boolean.valueOf(request.getParameter(PARAM_FORCE_INSTANT_EXECUTION.name)));
- }
-
- String overrideGlobalTimeoutVal = this.disableRequestConfiguration ? null : request.getParameter(PARAM_OVERRIDE_GLOBAL_TIMEOUT.name);
- if (StringUtils.isNotBlank(overrideGlobalTimeoutVal)) {
- executionOptions.setOverrideGlobalTimeout(Integer.valueOf(overrideGlobalTimeoutVal));
- } else if(servletDefaultTimeout > -1) {
- executionOptions.setOverrideGlobalTimeout((int) servletDefaultTimeout);
- }
-
- List<HealthCheckExecutionResult> executionResults = this.healthCheckExecutor.execute(selector, executionOptions);
-
- CombinedExecutionResult combinedExecutionResult = new CombinedExecutionResult(executionResults);
- Result overallResult = combinedExecutionResult.getHealthCheckResult();
-
- sendNoCacheHeaders(response);
- sendCorsHeaders(response);
-
- Integer httpStatus = statusMapping.get(overallResult.getStatus());
- response.setStatus(httpStatus);
-
- response.setHeader(STATUS_HEADER_NAME, overallResult.getStatus().toString());
-
- final boolean formatAllowed = this.isFormatAllowed(format);
-
- if (formatAllowed && FORMAT_HTML.equals(format)) {
- sendHtmlResponse(overallResult, executionResults, request, response, includeDebug);
- } else if (formatAllowed && FORMAT_JSON.equals(format)) {
- sendJsonResponse(overallResult, executionResults, null, response, includeDebug);
- } else if (formatAllowed && FORMAT_JSONP.equals(format)) {
- String jsonpCallback = StringUtils.defaultIfBlank(request.getParameter(PARAM_JSONP_CALLBACK.name), JSONP_CALLBACK_DEFAULT);
- sendJsonResponse(overallResult, executionResults, jsonpCallback, response, includeDebug);
- } else if (formatAllowed && format != null && format.endsWith(FORMAT_TXT)) {
- sendTxtResponse(overallResult, response, FORMAT_VERBOSE_TXT.equals(format), executionResults, includeDebug);
- } else {
- response.setContentType("text/plain");
- response.getWriter().println("Invalid format " + format + " - supported formats: " + Arrays.toString(this.allowedFormats));
- }
- }
-
- @Override
- protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
- final String[] splitPathInfo = splitFormat(request.getPathInfo());
- String format = splitPathInfo[1];
- if (format == null) {
- // if not provided via extension use parameter or default
- format = StringUtils.defaultIfBlank(request.getParameter(PARAM_FORMAT.name), defaultFormat);
- }
-
- String pathTokensStr = splitPathInfo[0];
- if (pathTokensStr!=null && pathTokensStr.startsWith("/")) {
- pathTokensStr = pathTokensStr.substring(1, pathTokensStr.length());
- }
-
- doGet(request, response, pathTokensStr, format);
- }
-
- String[] splitFormat(final String pathInfo) {
- if ( pathInfo != null ) {
- for (String format : new String[] { FORMAT_HTML, FORMAT_JSON, FORMAT_JSONP, FORMAT_VERBOSE_TXT, FORMAT_TXT }) {
- final String formatWithDot = ".".concat(format);
- if (pathInfo.endsWith(formatWithDot)) {
- return new String[] { pathInfo.substring(0, pathInfo.length() - formatWithDot.length()), format };
- }
- }
- }
- return new String[] { pathInfo, null };
- }
-
- private void sendTxtResponse(final Result overallResult, final HttpServletResponse response, boolean verbose,
- List<HealthCheckExecutionResult> executionResults, boolean includeDebug) throws IOException {
- response.setContentType(CONTENT_TYPE_TXT);
- response.setCharacterEncoding("UTF-8");
- if (verbose) {
- response.getWriter().write(verboseTxtSerializer.serialize(overallResult, executionResults, includeDebug));
- } else {
- response.getWriter().write(txtSerializer.serialize(overallResult));
- }
- }
-
- private void sendJsonResponse(final Result overallResult, final List<HealthCheckExecutionResult> executionResults,
- final String jsonpCallback,
- final HttpServletResponse response, boolean includeDebug)
- throws IOException {
- if (StringUtils.isNotBlank(jsonpCallback)) {
- response.setContentType(CONTENT_TYPE_JSONP);
- } else {
- response.setContentType(CONTENT_TYPE_JSON);
- }
- response.setCharacterEncoding("UTF-8");
-
- String resultJson = this.jsonSerializer.serialize(overallResult, executionResults, jsonpCallback, includeDebug);
- PrintWriter writer = response.getWriter();
- writer.append(resultJson);
- }
-
- private void sendHtmlResponse(final Result overallResult, final List<HealthCheckExecutionResult> executionResults,
- final HttpServletRequest request, final HttpServletResponse response, boolean includeDebug)
- throws IOException {
- response.setContentType(CONTENT_TYPE_HTML);
- response.setCharacterEncoding("UTF-8");
-
- List<Param> allowedParameters = disableRequestConfiguration ? Arrays.asList(PARAM_FORMAT) : Arrays.asList(PARAM_LIST);
- response.getWriter().append(this.htmlSerializer.serialize(overallResult, executionResults, allowedParameters, includeDebug));
- }
-
- private void sendNoCacheHeaders(final HttpServletResponse response) {
- response.setHeader(CACHE_CONTROL_KEY, CACHE_CONTROL_VALUE);
- }
-
- private void sendCorsHeaders(final HttpServletResponse response) {
- if (StringUtils.isNotBlank(corsAccessControlAllowOrigin)) {
- response.setHeader(CORS_ORIGIN_HEADER_NAME, corsAccessControlAllowOrigin);
- }
- }
-
- Map<Result.Status, Integer> getStatusMapping(String mappingStr) {
- Map<Result.Status, Integer> statusMapping = new TreeMap<Result.Status, Integer>();
-
- try {
- String[] bits = mappingStr.split("[,]");
- for (String bit : bits) {
- String[] tuple = bit.split("[:]");
- statusMapping.put(Result.Status.valueOf(tuple[0]), Integer.parseInt(tuple[1]));
- }
- } catch (Exception e) {
- throw new IllegalArgumentException("Invalid parameter httpStatus=" + mappingStr + " " + e, e);
- }
-
- if (!statusMapping.containsKey(Result.Status.OK)) {
- statusMapping.put(Result.Status.OK, 200);
- }
- if (!statusMapping.containsKey(Result.Status.WARN)) {
- statusMapping.put(Result.Status.WARN, statusMapping.get(Result.Status.OK));
- }
- if (!statusMapping.containsKey(Result.Status.TEMPORARILY_UNAVAILABLE)) {
- statusMapping.put(Result.Status.TEMPORARILY_UNAVAILABLE, 503);
- }
- if (!statusMapping.containsKey(Result.Status.CRITICAL)) {
- statusMapping.put(Result.Status.CRITICAL, 503);
- }
- if (!statusMapping.containsKey(Result.Status.HEALTH_CHECK_ERROR)) {
- statusMapping.put(Result.Status.HEALTH_CHECK_ERROR, 500);
- }
- return statusMapping;
- }
-
- private class ProxyServlet extends HttpServlet {
- private static final long serialVersionUID = 1L;
-
- private final String format;
-
- private ProxyServlet(final String format) {
- this.format = format;
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- HealthCheckExecutorServlet.this.doGet(req, resp, null, format);
- }
- }
-
- private static class ServletInfoDTO extends DTO {
- String contextName;
- String whiteboardTarget;
- String servletPath;
- Servlet servlet;
-
- public ServletInfoDTO(String contextName, String whiteboardTarget, String servletPath, Servlet servlet) {
- this.contextName = contextName;
- this.whiteboardTarget = whiteboardTarget;
- 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
deleted file mode 100644
index 5cff0a2d7f..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/HealthCheckExecutorServletConfiguration.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.servlet;
-
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-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 to register the servlet with. If not specified, the default context is used.")
- String servletContextName();
-
- @AttributeDefinition(name = "Servlet Path", description = "Servlet path (defaults to " + SERVLET_PATH_DEFAULT + ")")
- String servletPath() default SERVLET_PATH_DEFAULT;
-
- @AttributeDefinition(name = "Servlet Whiteboard Filter", description = "Needs to be set to correct servlet whiteboard filter expression")
- String osgi_http_whiteboard_target();
-
- @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";
-
- @AttributeDefinition(name = "Timeout", description = "Timeout for health check executor. If not configured (left to -1), the default from health check executor's configuration is taken. The setting can always be overwritten by request parameter 'timeout'")
- long timeout() default -1;
-
- @AttributeDefinition(name = "Default Tags", description = "Default tags if no tags are provided in URL.")
- String[] tags() default {};
-
- @AttributeDefinition(name = "Combine Tags with OR", description = "If true, will execute checks that have any of the given tags. If false, will only execute checks that have *all* of the given tags.")
- boolean combineTagsWithOr() default true;
-
- @AttributeDefinition(name = "Default Format", description = "Default format if format is not provided in URL",
- options = {
- @Option(label = "HTML", value = HealthCheckExecutorServlet.FORMAT_HTML),
- @Option(label = "JSON", value = HealthCheckExecutorServlet.FORMAT_JSON),
- @Option(label = "JSONP", value = HealthCheckExecutorServlet.FORMAT_JSONP),
- @Option(label = "TXT", value = HealthCheckExecutorServlet.FORMAT_TXT),
- @Option(label = "VERBOSE TXT", value = HealthCheckExecutorServlet.FORMAT_VERBOSE_TXT)
- })
- String format() default HealthCheckExecutorServlet.FORMAT_HTML;
-
- @AttributeDefinition(name = "Allowed Formats", description = "Allow list for formats passed in via the URL",
- options = {
- @Option(label = "HTML", value = HealthCheckExecutorServlet.FORMAT_HTML),
- @Option(label = "JSON", value = HealthCheckExecutorServlet.FORMAT_JSON),
- @Option(label = "JSONP", value = HealthCheckExecutorServlet.FORMAT_JSONP),
- @Option(label = "TXT", value = HealthCheckExecutorServlet.FORMAT_TXT),
- @Option(label = "VERBOSE TXT", value = HealthCheckExecutorServlet.FORMAT_VERBOSE_TXT)
- })
- String[] allowed_formats() default {
- HealthCheckExecutorServlet.FORMAT_HTML,
- HealthCheckExecutorServlet.FORMAT_JSON,
- HealthCheckExecutorServlet.FORMAT_JSONP,
- HealthCheckExecutorServlet.FORMAT_TXT,
- HealthCheckExecutorServlet.FORMAT_VERBOSE_TXT
- };
-
- @AttributeDefinition(name = "Disabled", description = "Allows to disable the servlet if required for security reasons")
- boolean disabled() default false;
-
- @AttributeDefinition(name = "CORS Access-Control-Allow-Origin", description = "Sets the Access-Control-Allow-Origin CORS header. If blank no header is sent.")
- String cors_accessControlAllowOrigin() default "*";
-
- @AttributeDefinition(name = "Disable Request Configuration", description = "If set, parameters passed in via the request are ignored (except for format)")
- boolean disable_request_configuration() default false;
-
- @AttributeDefinition
- String webconsole_configurationFactory_nameHint() default "{servletPath} default format:{format} default tags:{tags} ";
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultHtmlSerializer.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultHtmlSerializer.java
deleted file mode 100644
index 6b5c7bb47c..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultHtmlSerializer.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.servlet;
-
-import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.ResultLog.Entry;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.apache.felix.hc.core.impl.servlet.HealthCheckExecutorServlet.Param;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.ComponentConstants;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-
-/** Serializes health check results into html format. */
-@Component(service = ResultHtmlSerializer.class)
-public class ResultHtmlSerializer {
-
- private String styleString;
-
- @Activate
- protected final void activate(final ResultHtmlSerializerConfiguration configuration) {
- this.styleString = configuration.styleString();
- }
-
- public String serialize(final Result overallResult, final List<HealthCheckExecutionResult> executionResults, List<Param> paramList,
- boolean includeDebug) {
-
- StringWriter stringWriter = new StringWriter();
- PrintWriter writer = new PrintWriter(stringWriter);
-
- writer.println("<html><head><title>System Health</title>" +
- "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /><style>" + styleString +
- "</style></head><body><h1>System Health</h1>");
-
- writer.println("<p><span class=\"" + getClassForStatus(overallResult.getStatus()) + "\" style=\"padding:4px\"><strong>Overall Result: "
- + overallResult.getStatus() + "</strong></span></p>");
-
- final DateFormat dfShort = new SimpleDateFormat("HH:mm:ss.SSS");
- final DateFormat dfLong = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-
- writer.println("<table id=\"healthCheckResults\" cellspacing=\"0\">");
- writer.println(
- "<thead><tr><th>Health Check <span style='color:gray'>(tags)</span></th><th>Status</th><th>Log</th><th>Finished At</th><th>Time</th></tr></thead>");
- for (HealthCheckExecutionResult executionResult : executionResults) {
- Result result = executionResult.getHealthCheckResult();
- List<String> tags = executionResult.getHealthCheckMetadata().getTags();
- boolean hasTags = tags != null && tags.size() > 0 && StringUtils.isNotBlank(tags.get(0));
- writer.print("<tr class=\"" + getClassForStatus(result.getStatus()) + "\">");
- String tooltipHint = getTooltipHint(executionResult.getHealthCheckMetadata());
- writer.print("<td><p title=\"" + escapeHtml(tooltipHint) + "\">"
- + escapeHtml(executionResult.getHealthCheckMetadata().getTitle()) + "");
- if (hasTags) {
- writer.println("<br/><span style='color:gray'>" + escapeHtml(String.join(", ", tags)) + "</span>");
- }
- writer.println("</p></td>");
- writer.println("<td style='font-weight:bold;'>" + escapeHtml(result.getStatus().toString()) + "</td>");
- writer.println("<td>");
- boolean isFirst = true;
-
- boolean isSingleResult = isSingleResult(result);
-
- for (Entry entry : result) {
- if (!includeDebug && entry.isDebug()) {
- continue;
- }
-
- if (isFirst) {
- isFirst = false;
- } else {
- writer.println("<br/>\n");
- }
-
- boolean showStatus = !isSingleResult && !entry.isDebug() && entry.getStatus() != Result.Status.OK;
-
- String message = escapeHtml(entry.getMessage());
- if (entry.isDebug()) {
- message = "<span style='color:gray'/>" + message + "</span>";
- }
- writer.println((showStatus ? escapeHtml(entry.getStatus().toString()) + " " : "") + message);
-
- Exception exception = entry.getException();
- if (exception != null) {
- writer.println("<span style='width:20px'/>" + escapeHtml(exception.toString()));
- writer.println("<!--");
- exception.printStackTrace(writer);
- writer.println("-->");
- }
- }
- writer.println("</td>");
- Date finishedAt = executionResult.getFinishedAt();
- writer.println("<td>" + (isToday(finishedAt) ? dfShort.format(finishedAt) : dfLong.format(finishedAt)) + "</td>");
- writer.println("<td>" + msHumanReadable(executionResult.getElapsedTimeInMs()) + "</td>");
-
- writer.println("</tr>");
- }
- writer.println("</table>");
-
- writer.println("<div class='helpText'>");
- writer.println(getHtmlHelpText(paramList));
- writer.println("</div>");
- writer.println("</body></html>");
-
- return stringWriter.toString();
-
- }
-
- private String getTooltipHint(HealthCheckMetadata healthCheckMetadata) {
- List<String> hints = new ArrayList<>();
- ServiceReference<?> serviceReference = healthCheckMetadata.getServiceReference();
- Object compName = serviceReference.getProperty(ComponentConstants.COMPONENT_NAME);
- if(compName != null) {
- hints.add("Component Name: "+compName);
- }
- Bundle bundle = serviceReference.getBundle();
- if(bundle != null) {
- hints.add("Bundle: "+bundle.getSymbolicName());
- }
- return String.join(" ", hints);
- }
-
- private String getClassForStatus(final Result.Status status) {
- return "status" + status.name();
- }
-
- private boolean isSingleResult(final Result result) {
- int count = 0;
- for (Entry entry : result) {
- count++;
- if (count > 1) {
- return false;
- }
- }
- return true;
- }
-
- private boolean isToday(Date date) {
- Calendar cal1 = Calendar.getInstance();
- Calendar cal2 = Calendar.getInstance();
- cal2.setTime(date);
- boolean isToday = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
- cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
- return isToday;
-
- }
-
- private String getHtmlHelpText(List<Param> paramList) {
- final StringBuilder sb = new StringBuilder();
- sb.append("<h3>Supported URL parameters</h3>\n");
- for (Param p : paramList) {
- sb.append("<b>").append(p.name).append("</b>:");
- sb.append(escapeHtml(p.description));
- sb.append("<br/>");
- }
- return sb.toString();
- }
-
- private String escapeHtml(String text) {
- if(text == null) {
- return null;
- }
- return text
- .replace("&", "&")
- .replace("\"", """)
- .replace("<", "<")
- .replace(">", ">");
- }
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultHtmlSerializerConfiguration.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultHtmlSerializerConfiguration.java
deleted file mode 100644
index 392e4fec8a..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultHtmlSerializerConfiguration.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.servlet;
-
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@ObjectClassDefinition(name = "Apache Felix Health Check Result HTML Serializer", description = "Serializer for health check results in HTML format")
-@interface ResultHtmlSerializerConfiguration {
-
- String CSS_STYLE_DEFAULT = "body { font-size:12px; font-family:arial,verdana,sans-serif;background-color:#FFFDF1; }\n"
- + "h1 { font-size:20px;}\n"
- + "table { font-size:12px; border:#ccc 1px solid; border-radius:3px; }\n"
- + "table th { padding:5px; text-align: left; background: #ededed; }\n"
- + "table td { padding:5px; border-top: 1px solid #ffffff; border-bottom:1px solid #e0e0e0; border-left: 1px solid #e0e0e0; }\n"
- + ".statusOK { background-color:#CCFFCC;}\n"
- + ".statusWARN { background-color:#FFE569;}\n"
- + ".statusTEMPORARILY_UNAVAILABLE { background-color:#dab6fc;}\n"
- + ".statusCRITICAL { background-color:#F0975A;}\n"
- + ".statusHEALTH_CHECK_ERROR { background-color:#F16D4E;}\n"
- + ".helpText { color:grey; font-size:80%; }\n";
-
- @AttributeDefinition(name = "CSS Style", description = "CSS Style - can be configured to change the look and feel of the html result page.")
- String styleString() default CSS_STYLE_DEFAULT;
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultJsonSerializer.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultJsonSerializer.java
deleted file mode 100644
index 61eefe9964..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultJsonSerializer.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.servlet;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.text.SimpleDateFormat;
-import java.util.List;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.apache.felix.utils.json.JSONWriter;
-import org.osgi.service.component.annotations.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Serializes health check results into json format. */
-@Component(service = ResultJsonSerializer.class)
-public class ResultJsonSerializer {
-
- private static final Logger LOG = LoggerFactory.getLogger(ResultJsonSerializer.class);
-
- static final String OVERALL_RESULT_KEY = "OverallResult";
-
- public String serialize(final Result overallResult, final List<HealthCheckExecutionResult> executionResults, final String jsonpCallback,
- boolean includeDebug) {
-
- LOG.debug("Sending json response... ");
-
- StringWriter writer = new StringWriter();
- try {
- JSONWriter jsonWriter = new JSONWriter(writer);
- jsonWriter.object();
- jsonWriter.key("overallResult");
- jsonWriter.value(overallResult.getStatus().toString());
- jsonWriter.key("results");
- jsonWriter.array();
- for (HealthCheckExecutionResult healthCheckResult : executionResults) {
- writeResult(healthCheckResult, includeDebug, jsonWriter);
- }
- jsonWriter.endArray();
- jsonWriter.endObject();
- } catch(IOException e) {
- LOG.error("Could not serialise health check result: e="+e, e);
- writer.write("{error:'"+e.getMessage()+"'}");
- }
- String resultStr = writer.toString();
-
- if (StringUtils.isNotBlank(jsonpCallback)) {
- resultStr = jsonpCallback + "(" + resultStr + ");";
- }
-
- return resultStr;
-
- }
-
- private void writeResult(final HealthCheckExecutionResult healthCheckResult, boolean includeDebug, JSONWriter jsonWriter) throws IOException {
-
- jsonWriter.object()
- .key("name").value(healthCheckResult.getHealthCheckMetadata().getTitle())
- .key("status").value(healthCheckResult.getHealthCheckResult().getStatus().toString())
- .key("timeInMs").value(healthCheckResult.getElapsedTimeInMs())
- .key("finishedAt").value(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").format(healthCheckResult.getFinishedAt())) ;
-
- jsonWriter.key("tags").array();
- for(String tag: healthCheckResult.getHealthCheckMetadata().getTags()) {
- jsonWriter.value(tag);
- }
- jsonWriter.endArray();
-
- jsonWriter.key("messages").array();
- for (ResultLog.Entry entry : healthCheckResult.getHealthCheckResult()) {
- if (!includeDebug && entry.isDebug()) {
- continue;
- }
- jsonWriter.object()
- .key("status").value(entry.getStatus().toString())
- .key("message").value(entry.getMessage());
-
- Exception exception = entry.getException();
- if (exception != null) {
- StringWriter stringWriter = new StringWriter();
- exception.printStackTrace(new PrintWriter(stringWriter));
- jsonWriter.key("exception").value(stringWriter.toString());
- }
- jsonWriter.endObject();
- }
- jsonWriter.endArray();
-
-
- jsonWriter.endObject();
- }
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtSerializer.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtSerializer.java
deleted file mode 100644
index eda14b5cee..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtSerializer.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.servlet;
-
-import org.apache.felix.hc.api.Result;
-import org.osgi.service.component.annotations.Component;
-
-/** Serializes health check results into a simple text message (ideal to be used by a load balancer that would discard further
- * information). */
-@Component(service = ResultTxtSerializer.class)
-public class ResultTxtSerializer {
- public String serialize(final Result overallResult) {
- return overallResult.getStatus().toString();
- }
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializer.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializer.java
deleted file mode 100644
index f5bbc8fd0e..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializer.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.servlet;
-
-import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.List;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Serializes health check results into a verbose text message. */
-@Component(service = ResultTxtVerboseSerializer.class)
-public class ResultTxtVerboseSerializer {
-
- private static final Logger LOG = LoggerFactory.getLogger(ResultTxtVerboseSerializer.class);
-
- private static final String NEWLINE = "\n"; // not using system prop 'line.separator' as not the local but the calling system is
- // relevant.
-
- private int totalWidth;
-
- private int colWidthName;
-
- private int colWidthResult;
-
- private int colWidthTiming;
-
- private int colWidthWithoutLog;
- private int colWidthLog;
-
- @Activate
- protected final void activate(final ResultTxtVerboseSerializerConfiguration configuration) {
- this.totalWidth = configuration.totalWidth();
- this.colWidthName = configuration.colWidthName();
- this.colWidthResult = configuration.colWidthResult();
- this.colWidthTiming = configuration.colWidthTiming();
- colWidthWithoutLog = colWidthName + colWidthResult + colWidthTiming;
- colWidthLog = totalWidth - colWidthWithoutLog;
- }
-
- // without overall result, only the execution results are listed without header
- public String serialize(String label, final List<HealthCheckExecutionResult> executionResults, boolean includeDebug) {
- StringBuilder resultStr = new StringBuilder();
-
- resultStr.append(label + "\n");
- resultStr.append(serializeResults(executionResults, includeDebug));
-
- return resultStr.toString();
- }
-
- public String serialize(final Result overallResult, final List<HealthCheckExecutionResult> executionResults, boolean includeDebug) {
-
- StringBuilder resultStr = new StringBuilder();
-
- resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
- resultStr.append(
- center("Overall Health Result: " + overallResult.getStatus().toString(), totalWidth) + NEWLINE);
- resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
-
- resultStr.append(rightPad("Name", colWidthName));
- resultStr.append(rightPad("Result", colWidthResult));
- resultStr.append(rightPad("Timing", colWidthTiming));
- resultStr.append("Logs" + NEWLINE);
- resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
-
- resultStr.append(serializeResults(executionResults, includeDebug));
- resultStr.append(StringUtils.repeat("-", totalWidth) + NEWLINE);
-
- return resultStr.toString();
-
- }
-
- private String serializeResults(final List<HealthCheckExecutionResult> executionResults, boolean includeDebug) {
-
- StringBuilder resultStr = new StringBuilder();
-
- final DateFormat dfShort = new SimpleDateFormat("HH:mm:ss.SSS");
-
- for (HealthCheckExecutionResult healthCheckResult : executionResults) {
- appendVerboseTxtForResult(resultStr, healthCheckResult, includeDebug, dfShort);
- }
-
- return resultStr.toString();
-
- }
-
- private void appendVerboseTxtForResult(StringBuilder resultStr, HealthCheckExecutionResult healthCheckResult, boolean includeDebug,
- DateFormat dfShort) {
-
- String wrappedName = wordWrap(healthCheckResult.getHealthCheckMetadata().getTitle(), colWidthName, "\n");
-
- int lastIndexOfNewline = wrappedName.lastIndexOf("\n");
- String relevantNameStringForPadding = lastIndexOfNewline >= 0 ? wrappedName.substring(lastIndexOfNewline+1) : wrappedName;
- int paddingSize = colWidthName - relevantNameStringForPadding.length();
-
- resultStr.append(wrappedName + StringUtils.repeat(" ", paddingSize));
- resultStr.append(rightPad(healthCheckResult.getHealthCheckResult().getStatus().toString(), colWidthResult));
- resultStr.append(rightPad("[" + dfShort.format(healthCheckResult.getFinishedAt())
- + "|" + msHumanReadable(healthCheckResult.getElapsedTimeInMs()) + "]", colWidthTiming));
-
- boolean isFirst = true;
- for (ResultLog.Entry logEntry : healthCheckResult.getHealthCheckResult()) {
- if (!includeDebug && logEntry.isDebug()) {
- continue;
- }
- if (isFirst) {
- isFirst = false;
- } else {
- resultStr.append(StringUtils.repeat(" ", colWidthWithoutLog));
- }
-
- String oneLineMessage = getStatusForTxtLog(logEntry) + logEntry.getMessage();
- String messageToPrint = wordWrap(oneLineMessage, colWidthLog, "\n" + StringUtils.repeat(" ", colWidthWithoutLog));
-
- resultStr.append(messageToPrint);
- resultStr.append(NEWLINE);
- }
-
- if (isFirst) {
- // no log entry exists, ensure newline
- resultStr.append(NEWLINE);
- }
-
- }
-
- private String getStatusForTxtLog(ResultLog.Entry logEntry) {
- if (logEntry.getStatus() == Result.Status.OK) {
- return "";
- } else {
- return logEntry.getStatus().toString() + " ";
- }
- }
-
- // -- Some simple helpers directly here as commons lang is removed
-
- String wordWrap(String s, int maxWidth, String newlineDelimiter) {
- return s.replaceAll("(.{1,"+maxWidth+"})(?: +|$)\\n?|(.{"+maxWidth+"})", "$1$2"+newlineDelimiter).trim();
- }
-
- public static String rightPad(String s, int size) {
- if(s.length() < size) {
- return s + StringUtils.repeat(" ", size - s.length());
- } else {
- return s;
- }
- }
-
- static String center(String s, int size) {
- if(s.length() < size) {
- int padding = size - s.length();
- int paddingLeft = padding / 2;
- int paddingRight = padding / 2;
- if(padding % 2 == 1) {
- paddingRight++;
- }
- return StringUtils.repeat(" ", paddingLeft) + s + StringUtils.repeat(" ", paddingRight);
- } else {
- return s;
- }
- }
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializerConfiguration.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializerConfiguration.java
deleted file mode 100644
index ac515f6795..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/servlet/ResultTxtVerboseSerializerConfiguration.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.servlet;
-
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@ObjectClassDefinition(name = "Apache Felix Health Check Verbose Text Serializer", description = "Serializes health check results to a verbose text format")
-@interface ResultTxtVerboseSerializerConfiguration {
-
- @AttributeDefinition(name = "Total Width", description = "Total width of all columns in verbose txt rendering (in characters)")
- int totalWidth() default 140;
-
- @AttributeDefinition(name = "Name Column Width", description = "Column width of health check name (in characters)")
- int colWidthName() default 30;
-
- @AttributeDefinition(name = "Result Column Width", description = "Column width of health check result (in characters)")
- int colWidthResult() default 25;
-
- @AttributeDefinition(name = "Timing Column Width", description = "Column width of health check timing (in characters)")
- int colWidthTiming() default 22;
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/AdhocStatusHealthCheck.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/AdhocStatusHealthCheck.java
deleted file mode 100644
index 2c1aeaa640..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/AdhocStatusHealthCheck.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.util;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.osgi.framework.ServiceRegistration;
-
-public class AdhocStatusHealthCheck implements HealthCheck {
-
- private Result result;
-
- private ServiceRegistration<HealthCheck> serviceRegistration;
-
- public AdhocStatusHealthCheck(Result.Status status, String msg) {
- result = new Result(status, msg);
- }
-
- @Override
- public Result execute() {
- return result;
- }
-
- public void updateMessage(String msg) {
- this.result = new Result(result.getStatus(), msg);
- }
-
- public void updateResult(Result result) {
- this.result = result;
- }
-
- public ServiceRegistration<HealthCheck> getServiceRegistration() {
- return serviceRegistration;
- }
-
- public void setServiceRegistration(ServiceRegistration<HealthCheck> serviceRegistration) {
- this.serviceRegistration = serviceRegistration;
- }
-
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/HealthCheckFilter.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/HealthCheckFilter.java
deleted file mode 100644
index 22f09d8702..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/HealthCheckFilter.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.util;
-
-import static org.apache.felix.hc.api.execution.HealthCheckSelector.empty;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Select from available {@link HealthCheck} services.
- *
- * This class is not thread safe and instances shouldn't be used concurrently from different threads. */
-public class HealthCheckFilter {
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- // object class (supporting current interface and legacy)
- public static final String HC_FILTER_OBJECT_CLASS = "(|(objectClass="+HealthCheck.class.getName()+")(objectClass=org.apache.sling.hc.api.HealthCheck))";
- public static final String OMIT_PREFIX = "-";
-
- private final BundleContext bundleContext;
-
- /** Create a new filter object
- *
- * @param bc bundle context*/
- public HealthCheckFilter(final BundleContext bc) {
- bundleContext = bc;
- }
-
- public ServiceReference<HealthCheck>[] getHealthCheckServiceReferences(final HealthCheckSelector selector) {
- return getHealthCheckServiceReferences(selector, false);
- }
-
- public ServiceReference<HealthCheck>[] getHealthCheckServiceReferences(final HealthCheckSelector selector, boolean combineTagsWithOr) {
- final CharSequence filterBuilder = selector != null ? getServiceFilter(selector, combineTagsWithOr)
- : getServiceFilter(empty(), combineTagsWithOr);
-
- log.trace("OSGi service filter in getHealthCheckServiceReferences(): {}", filterBuilder);
-
- try {
- final String filterString = filterBuilder.length() == 0 ? null : filterBuilder.toString();
- bundleContext.createFilter(filterString); // check syntax early
- final ServiceReference[] refs = bundleContext.getServiceReferences((String) null, filterString);
- if (refs == null) {
- log.debug("Found no HealthCheck services for filter: {}", filterString);
- return new ServiceReference[0];
- } else {
- log.debug("Found {} HealthCheck services for filter: {}", refs.length, filterString);
- }
- return refs;
- } catch (final InvalidSyntaxException ise) {
- // this should not happen, but we fail gracefully
- log.error("Invalid OSGi filter syntax in '" + filterBuilder + "'", ise);
- return new ServiceReference[0];
- }
- }
-
-
- CharSequence getServiceFilter(HealthCheckSelector selector, boolean combineTagsWithOr) {
- // Build service filter
- final StringBuilder filterBuilder = new StringBuilder();
- filterBuilder.append("(&");
- filterBuilder.append(HC_FILTER_OBJECT_CLASS);
-
- final int prefixLen = HealthCheckFilter.OMIT_PREFIX.length();
- final StringBuilder filterBuilderForOrOperator = new StringBuilder(); // or filters
- final StringBuilder tagsBuilder = new StringBuilder();
- int tagsAndClauses = 0;
- if (selector.tags() != null) {
- for (String tag : selector.tags()) {
- tag = tag.trim();
- if (tag.length() == 0) {
- continue;
- }
- if (tag.startsWith(HealthCheckFilter.OMIT_PREFIX)) {
- // ommit tags always have to be added as and-clause
- filterBuilder.append("(!(").append(HealthCheck.TAGS).append("=").append(tag.substring(prefixLen)).append("))");
- } else {
- // add regular tags in the list either to outer and-clause or inner or-clause
- if (combineTagsWithOr) {
- filterBuilderForOrOperator.append("(").append(HealthCheck.TAGS).append("=").append(tag).append(")");
- } else {
- tagsBuilder.append("(").append(HealthCheck.TAGS).append("=").append(tag).append(")");
- tagsAndClauses++;
- }
- }
- }
- }
- boolean addedNameToOrBuilder = false;
- if (selector.names() != null) {
- for (String name : selector.names()) {
- name = name.trim();
- if (name.length() == 0) {
- continue;
- }
- if (name.startsWith(HealthCheckFilter.OMIT_PREFIX)) {
- // ommit tags always have to be added as and-clause
- filterBuilder.append("(!(").append(HealthCheck.NAME).append("=").append(escapeOsgiFilterLiteral(name.substring(prefixLen))).append("))");
- } else {
- // names are always ORd
- filterBuilderForOrOperator.append("(").append(HealthCheck.NAME).append("=").append(escapeOsgiFilterLiteral(name)).append(")");
- addedNameToOrBuilder = true;
- }
- }
- }
- if (addedNameToOrBuilder) {
- if (tagsAndClauses > 1) {
- filterBuilderForOrOperator.append("(&").append(tagsBuilder).append(")");
- } else {
- filterBuilderForOrOperator.append(tagsBuilder);
- }
- } else {
- filterBuilder.append(tagsBuilder);
- }
- // add "or" clause if we have accumulated any
- if (filterBuilderForOrOperator.length() > 0) {
- filterBuilder.append("(|").append(filterBuilderForOrOperator).append(")");
- }
- filterBuilder.append(")");
- return filterBuilder;
- }
-
- private Object escapeOsgiFilterLiteral(String name) {
- return name.replace("*", "\\*").replace("(", "\\(").replace(")", "\\)");
- }
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/lang/StringUtils.java b/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/lang/StringUtils.java
deleted file mode 100644
index 2cb0e464c5..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/core/impl/util/lang/StringUtils.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.util.lang;
-
-/** Simple class for commons lang replacement where Java 8 does not provide replacements.
- *
- * Java 11 will allow to use String.isBlank() and String.repeat(), then this class can
- * be removed.
- *
- * HINT: This class is also used in generalchecks via Conditional-Package */
-public class StringUtils {
-
- public static boolean isBlank(final CharSequence cs) {
- return cs == null || cs.chars().allMatch(Character::isWhitespace);
- }
-
- public static boolean isNotBlank(final CharSequence cs) {
- return !isBlank(cs);
- }
-
- public static String defaultIfBlank(final String cs, String defaultStr) {
- return isBlank(cs) ? defaultStr : cs;
- }
-
- public static String repeat(String s, int n) {
- if(s == null) {
- return null;
- }
- final StringBuilder sb = new StringBuilder(s.length() * n);
- for(int i = 0; i < n; i++) {
- sb.append(s);
- }
- return sb.toString();
- }
-
-
-}
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/jmx/impl/HealthCheckMBean.java b/healthcheck/core/src/main/java/org/apache/felix/hc/jmx/impl/HealthCheckMBean.java
deleted file mode 100644
index 7b26cde8f2..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/jmx/impl/HealthCheckMBean.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.jmx.impl;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.management.Attribute;
-import javax.management.AttributeList;
-import javax.management.AttributeNotFoundException;
-import javax.management.DynamicMBean;
-import javax.management.InvalidAttributeValueException;
-import javax.management.MBeanAttributeInfo;
-import javax.management.MBeanException;
-import javax.management.MBeanInfo;
-import javax.management.ReflectionException;
-import javax.management.openmbean.CompositeDataSupport;
-import javax.management.openmbean.CompositeType;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
-import javax.management.openmbean.OpenType;
-import javax.management.openmbean.SimpleType;
-import javax.management.openmbean.TabularData;
-import javax.management.openmbean.TabularDataSupport;
-import javax.management.openmbean.TabularType;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-
-/** A {@link DynamicMBean} used to execute a {@link HealthCheck} service */
-public class HealthCheckMBean implements DynamicMBean {
-
- private static final String HC_OK_ATTRIBUTE_NAME = "ok";
- private static final String HC_STATUS_ATTRIBUTE_NAME = "status";
- private static final String HC_LOG_ATTRIBUTE_NAME = "log";
- private static final String HC_TIMED_OUT_ATTRIBUTE_NAME = "timedOut";
- private static final String HC_ELAPSED_TIMED_ATTRIBUTE_NAME = "elapsedTime";
- private static final String HC_FINISHED_AT_ATTRIBUTE_NAME = "finishedAt";
- private static CompositeType LOG_ROW_TYPE;
- private static TabularType LOG_TABLE_TYPE;
-
- private static final String INDEX_COLUMN = "index";
- private static final String LEVEL_COLUMN = "level";
- private static final String MESSAGE_COLUMN = "message";
-
- /** The health check service to call. */
- private final ServiceReference<HealthCheck> healthCheckRef;
-
- /** The executor service. */
- private final ExtendedHealthCheckExecutor executor;
-
- /** The mbean info. */
- private final MBeanInfo mbeanInfo;
-
- /** The default attributes. */
- private final Map<String, Object> defaultAttributes;
-
- static {
- try {
- // Define the log row and table types
- LOG_ROW_TYPE = new CompositeType(
- "LogLine",
- "A line in the result log",
- new String[] { INDEX_COLUMN, LEVEL_COLUMN, MESSAGE_COLUMN },
- new String[] { "log line index", "log level", "log message" },
- new OpenType[] { SimpleType.INTEGER, SimpleType.STRING, SimpleType.STRING });
- final String[] indexes = { INDEX_COLUMN };
- LOG_TABLE_TYPE = new TabularType("LogTable", "Result log messages", LOG_ROW_TYPE, indexes);
- } catch (Exception ignore) {
- // row or table type will be null if this happens
- }
- }
-
- public HealthCheckMBean(final ServiceReference<HealthCheck> ref, final ExtendedHealthCheckExecutor executor) {
- this.healthCheckRef = ref;
- this.executor = executor;
- this.mbeanInfo = this.createMBeanInfo(ref);
- this.defaultAttributes = this.createDefaultAttributes(ref);
- }
-
- @Override
- public Object getAttribute(final String attribute)
- throws AttributeNotFoundException, MBeanException, ReflectionException {
- // we should call getAttributes - and not vice versa to have the result
- // of a single check call - and not do a check call for each attribute
- final AttributeList result = this.getAttributes(new String[] { attribute });
- if (result.size() == 0) {
- throw new AttributeNotFoundException(attribute);
- }
- final Attribute attr = (Attribute) result.get(0);
- return attr.getValue();
- }
-
- private TabularData logData(final Result er) throws OpenDataException {
- final TabularDataSupport result = new TabularDataSupport(LOG_TABLE_TYPE);
- int i = 1;
- for (final ResultLog.Entry e : er) {
- final Map<String, Object> data = new HashMap<String, Object>();
- data.put(INDEX_COLUMN, i++);
- data.put(LEVEL_COLUMN, e.getLogLevel());
- data.put(MESSAGE_COLUMN, e.getMessage());
-
- result.put(new CompositeDataSupport(LOG_ROW_TYPE, data));
- }
- return result;
- }
-
- @Override
- public AttributeList getAttributes(final String[] attributes) {
- final AttributeList result = new AttributeList();
- if (attributes != null) {
- HealthCheckExecutionResult hcResult = null;
- for (final String key : attributes) {
- final Object defaultValue = this.defaultAttributes.get(key);
- if (defaultValue != null) {
- result.add(new Attribute(key, defaultValue));
- } else {
- // we assume that a valid attribute name is used
- // which is requesting a hc result
- if (hcResult == null) {
- hcResult = this.getHealthCheckResult();
- }
-
- if (HC_OK_ATTRIBUTE_NAME.equals(key)) {
- result.add(new Attribute(key, hcResult.getHealthCheckResult().isOk()));
- } else if (HC_LOG_ATTRIBUTE_NAME.equals(key)) {
- try {
- result.add(new Attribute(key, logData(hcResult.getHealthCheckResult())));
- } catch (final OpenDataException ignore) {
- // we ignore this and simply don't add the attribute
- }
- } else if (HC_STATUS_ATTRIBUTE_NAME.equals(key)) {
- result.add(new Attribute(key, hcResult.getHealthCheckResult().getStatus().toString()));
- } else if (HC_ELAPSED_TIMED_ATTRIBUTE_NAME.equals(key)) {
- result.add(new Attribute(key, hcResult.getElapsedTimeInMs()));
- } else if (HC_FINISHED_AT_ATTRIBUTE_NAME.equals(key)) {
- result.add(new Attribute(key, hcResult.getFinishedAt()));
- } else if (HC_TIMED_OUT_ATTRIBUTE_NAME.equals(key)) {
- result.add(new Attribute(key, hcResult.hasTimedOut()));
- }
- }
- }
- }
-
- return result;
- }
-
- /** Create the mbean info */
- private MBeanInfo createMBeanInfo(final ServiceReference<HealthCheck> serviceReference) {
- final List<MBeanAttributeInfo> attrs = new ArrayList<MBeanAttributeInfo>();
-
- // add relevant service properties
- if (serviceReference.getProperty(HealthCheck.NAME) != null) {
- attrs.add(new MBeanAttributeInfo(HealthCheck.NAME, String.class.getName(), "The name of the health check service.", true, false,
- false));
- }
- if (serviceReference.getProperty(HealthCheck.TAGS) != null) {
- attrs.add(new MBeanAttributeInfo(HealthCheck.TAGS, String.class.getName(), "The tags of the health check service.", true, false,
- false));
- }
-
- // add standard attributes
- attrs.add(new MBeanAttributeInfo(HC_OK_ATTRIBUTE_NAME, Boolean.class.getName(), "The health check result", true, false, false));
- attrs.add(new MBeanAttributeInfo(HC_STATUS_ATTRIBUTE_NAME, String.class.getName(), "The health check status", true, false, false));
- attrs.add(new MBeanAttributeInfo(HC_ELAPSED_TIMED_ATTRIBUTE_NAME, Long.class.getName(), "The elapsed time in miliseconds", true,
- false, false));
- attrs.add(new MBeanAttributeInfo(HC_FINISHED_AT_ATTRIBUTE_NAME, Date.class.getName(), "The date when the execution finished", true,
- false, false));
- attrs.add(new MBeanAttributeInfo(HC_TIMED_OUT_ATTRIBUTE_NAME, Boolean.class.getName(), "Indicates of the execution timed out", true,
- false, false));
- attrs.add(new OpenMBeanAttributeInfoSupport(HC_LOG_ATTRIBUTE_NAME, "The health check result log", LOG_TABLE_TYPE, true, false,
- false));
-
- final String description;
- if (serviceReference.getProperty(Constants.SERVICE_DESCRIPTION) != null) {
- description = serviceReference.getProperty(Constants.SERVICE_DESCRIPTION).toString();
- } else {
- description = "Health check";
- }
- return new MBeanInfo(this.getClass().getName(),
- description,
- attrs.toArray(new MBeanAttributeInfo[attrs.size()]), null, null, null);
- }
-
- /** Create the default attributes. */
- private Map<String, Object> createDefaultAttributes(final ServiceReference<HealthCheck> serviceReference) {
- final Map<String, Object> list = new HashMap<String, Object>();
- if (serviceReference.getProperty(HealthCheck.NAME) != null) {
- list.put(HealthCheck.NAME, serviceReference.getProperty(HealthCheck.NAME).toString());
- }
- if (serviceReference.getProperty(HealthCheck.TAGS) != null) {
- final Object value = serviceReference.getProperty(HealthCheck.TAGS);
- if (value instanceof String[]) {
- list.put(HealthCheck.TAGS, Arrays.toString((String[]) value));
- } else {
- list.put(HealthCheck.TAGS, value.toString());
- }
- }
-
- return list;
- }
-
- @Override
- public MBeanInfo getMBeanInfo() {
- return this.mbeanInfo;
- }
-
- @Override
- public Object invoke(final String actionName, final Object[] params, final String[] signature)
- throws MBeanException, ReflectionException {
- throw new MBeanException(new UnsupportedOperationException(getClass().getSimpleName() + " does not support operations."));
- }
-
- @Override
- public void setAttribute(final Attribute attribute)
- throws AttributeNotFoundException, InvalidAttributeValueException,
- MBeanException, ReflectionException {
- throw new MBeanException(new UnsupportedOperationException(getClass().getSimpleName() + " does not support setting attributes."));
- }
-
- @Override
- public AttributeList setAttributes(final AttributeList attributes) {
- return new AttributeList();
- }
-
- @Override
- public String toString() {
- return "HealthCheckMBean [healthCheck=" + this.healthCheckRef + "]";
- }
-
- private HealthCheckExecutionResult getHealthCheckResult() {
- return this.executor.execute(this.healthCheckRef);
- }
-}
\ No newline at end of file
diff --git a/healthcheck/core/src/main/java/org/apache/felix/hc/jmx/impl/HealthCheckMBeanCreator.java b/healthcheck/core/src/main/java/org/apache/felix/hc/jmx/impl/HealthCheckMBeanCreator.java
deleted file mode 100644
index 38c3b25354..0000000000
--- a/healthcheck/core/src/main/java/org/apache/felix/hc/jmx/impl/HealthCheckMBeanCreator.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.jmx.impl;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import javax.management.DynamicMBean;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor;
-import org.apache.felix.hc.core.impl.util.HealthCheckFilter;
-import org.apache.felix.hc.core.impl.util.lang.StringUtils;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Filter;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-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.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.util.tracker.ServiceTracker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/** Creates an {@link HealthCheckMBean} for every {@link HealthCheckMBean} service */
-@Component
-public class HealthCheckMBeanCreator {
-
- private static final String JMX_TYPE_NAME = "HealthCheck";
- private static final String JMX_DOMAIN = "org.apache.felix.healthcheck";
-
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- private final Map<ServiceReference<HealthCheck>, Registration> registeredServices = new HashMap<>();
-
- private final Map<String, List<ServiceReference<HealthCheck>>> sortedRegistrations = new HashMap<>();
-
- private ServiceTracker<HealthCheck, ?> hcTracker;
-
- @Reference
- private ExtendedHealthCheckExecutor executor;
-
- @Activate
- protected void activate(final BundleContext btx) {
- try {
- Filter filter = btx.createFilter("(&"+HealthCheckFilter.HC_FILTER_OBJECT_CLASS+"("+HealthCheck.MBEAN_NAME+"=*))");
- this.hcTracker = new HealthCheckServiceTracker(btx, filter);
- this.hcTracker.open();
- } catch (InvalidSyntaxException e) {
- logger.warn("Could not create service tracker for filter "+HealthCheckFilter.HC_FILTER_OBJECT_CLASS, e);
- }
-
- }
-
- @Deactivate
- protected void deactivate() {
- if (this.hcTracker != null) {
- this.hcTracker.close();
- this.hcTracker = null;
- }
- }
-
- /** Register an mbean for a health check service. The mbean is only registered if - the service has an mbean registration property - if
- * there is no other service with the same name but a higher service ranking
- *
- * @param bundleContext The bundle context
- * @param reference The service reference to the health check service
- * @return The registered mbean or <code>null</code> */
- private synchronized Object registerHCMBean(final BundleContext bundleContext, final ServiceReference<HealthCheck> reference) {
- final Registration reg = getRegistration(reference);
- if (reg != null) {
- this.registeredServices.put(reference, reg);
-
- List<ServiceReference<HealthCheck>> registered = this.sortedRegistrations.get(reg.name);
- if (registered == null) {
- registered = new ArrayList<>();
- this.sortedRegistrations.put(reg.name, registered);
- }
- registered.add(reference);
- // sort orders the references with lowest ranking first
- // we want the highest!
- Collections.sort(registered);
- final int lastIndex = registered.size() - 1;
- if (registered.get(lastIndex).equals(reference)) {
- if (registered.size() > 1) {
- final ServiceReference<HealthCheck> prevRef = registered.get(lastIndex - 1);
- final Registration prevReg = this.registeredServices.get(prevRef);
- prevReg.unregister();
- }
- reg.register(bundleContext);
- }
- }
- return reg;
- }
-
- private synchronized void unregisterHCMBean(final BundleContext bundleContext, final ServiceReference<HealthCheck> ref) {
- final Registration reg = registeredServices.remove(ref);
- if (reg != null) {
- final boolean registerFirst = reg.unregister();
- final List<ServiceReference<HealthCheck>> registered = this.sortedRegistrations.get(reg.name);
- registered.remove(ref);
- if (registered.size() == 0) {
- this.sortedRegistrations.remove(reg.name);
- } else if (registerFirst) {
- final ServiceReference<HealthCheck> newRef = registered.get(0);
- final Registration newReg = this.registeredServices.get(newRef);
- newReg.register(bundleContext);
- }
- bundleContext.ungetService(ref);
- }
- }
-
- private final class HealthCheckServiceTracker extends ServiceTracker<HealthCheck, Object> {
- private final BundleContext bundleContext;
-
- private HealthCheckServiceTracker(BundleContext bundleContext, Filter filter) {
- super(bundleContext, filter, null);
- this.bundleContext = bundleContext;
- }
-
- @Override
- public Object addingService(final ServiceReference<HealthCheck> reference) {
- return registerHCMBean(bundleContext, reference);
- }
-
- @Override
- public void modifiedService(final ServiceReference<HealthCheck> reference,
- final Object service) {
- unregisterHCMBean(bundleContext, reference);
- registerHCMBean(bundleContext, reference);
- }
-
- @Override
- public void removedService(final ServiceReference<HealthCheck> reference,
- final Object service) {
- unregisterHCMBean(bundleContext, reference);
- }
- }
-
- private final class Registration {
- private final String name;
- private final HealthCheckMBean mbean;
-
- private final String objectName;
-
- private ServiceRegistration<DynamicMBean> registration;
-
- Registration(final String name, final HealthCheckMBean mbean) {
- this.name = name;
- this.mbean = mbean;
- objectName = String.format("%s:type=%s,name=%s", JMX_DOMAIN, JMX_TYPE_NAME, name);
- }
-
- void register(final BundleContext btx) {
- logger.debug("Registering health check mbean {} with name {}", mbean, objectName);
- final Dictionary<String, String> mbeanProps = new Hashtable<String, String>();
- mbeanProps.put("jmx.objectname", objectName);
- this.registration = btx.registerService(DynamicMBean.class, this.mbean, mbeanProps);
- }
-
- boolean unregister() {
- if (this.registration != null) {
- logger.debug("Unregistering health check mbean {} with name {}", mbean, objectName);
- this.registration.unregister();
- this.registration = null;
- return true;
- }
- return false;
- }
- }
-
- private Registration getRegistration(final ServiceReference<HealthCheck> ref) {
- final String hcMBeanName = (String) ref.getProperty(HealthCheck.MBEAN_NAME);
- if (StringUtils.isNotBlank(hcMBeanName)) {
- final HealthCheckMBean mbean = new HealthCheckMBean(ref, executor);
- return new Registration(hcMBeanName.replace(',', '.'), mbean);
- } else {
- logger.warn("Service reference {} unexpectedly has property {} not set", ref, HealthCheck.MBEAN_NAME);
- return null;
- }
- }
-
-}
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/CompositeHealthCheckTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/CompositeHealthCheckTest.java
deleted file mode 100644
index e3f7d4430f..0000000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/CompositeHealthCheckTest.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionOptions;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckExecutor;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.executor.ExecutionResult;
-import org.apache.felix.hc.core.impl.util.HealthCheckFilter;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Matchers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.ComponentConstants;
-import org.osgi.service.component.ComponentContext;
-
-public class CompositeHealthCheckTest {
-
- @Spy
- private CompositeHealthCheck compositeHealthCheck = new CompositeHealthCheck();
-
- @Mock
- private HealthCheckExecutor healthCheckExecutor;
-
- @Mock
- private ComponentContext componentContext;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- compositeHealthCheck.setHealthCheckExecutor(healthCheckExecutor);
- compositeHealthCheck.setFilterTags(new String[] {});
- compositeHealthCheck.setComponentContext(componentContext);
- }
-
- @Test
- public void testExecution() {
-
- doReturn((Result) null).when(compositeHealthCheck).checkForRecursion(Matchers.<ServiceReference> any(),
- Matchers.<Set<String>> any());
- String[] testTags = new String[] { "tag1" };
- compositeHealthCheck.setFilterTags(testTags);
-
- List<HealthCheckExecutionResult> executionResults = new LinkedList<HealthCheckExecutionResult>();
- executionResults.add(createExecutionResult("Check 1", testTags, new Result(Result.Status.OK, "Good")));
- executionResults.add(createExecutionResult("Check 2", testTags, new Result(Result.Status.CRITICAL, "Bad")));
-
- when(healthCheckExecutor.execute(any(HealthCheckSelector.class), any(HealthCheckExecutionOptions.class)))
- .thenReturn(executionResults);
-
- Result result = compositeHealthCheck.execute();
-
- verify(healthCheckExecutor, times(1)).execute(argThat(selectorWithTags(testTags)), argThat(andOptions));
-
- assertEquals(Result.Status.CRITICAL, result.getStatus());
-
- }
-
- private ArgumentMatcher<HealthCheckSelector> selectorWithTags(final String[] tags) {
- return new ArgumentMatcher<HealthCheckSelector>() {
- @Override
- public boolean matches(HealthCheckSelector healthCheckSelector) {
- return Arrays.equals(healthCheckSelector.tags(), tags) && healthCheckSelector.names() == null;
- }
-
- @Override
- public String toString() {
- return "a select with tags (" + Arrays.toString(tags) + ") and no names.";
- }
- };
- }
-
- private HealthCheckExecutionResult createExecutionResult(String name, String[] testTags, Result result) {
- HealthCheckExecutionResult healthCheckExecutionResult = new ExecutionResult(
- new HealthCheckMetadata(new DummyHcServiceReference(name, testTags,
- new String[0])),
- result, 0L);
- return healthCheckExecutionResult;
- }
-
- @Test
- public void testSimpleRecursion() {
-
- // composite check referencing itself
- final String[] filterTags = new String[] { "check1" };
- final DummyHcServiceReference hcRef = new DummyHcServiceReference("Check 1", new String[] { "check1" }, filterTags);
-
- // test check is hcRef
- doReturn(hcRef).when(componentContext).getServiceReference();
- compositeHealthCheck.setFilterTags(filterTags);
-
- compositeHealthCheck.setHealthCheckFilter(new HealthCheckFilter(null) {
-
- @Override
- public ServiceReference[] getHealthCheckServiceReferences(HealthCheckSelector selector) {
- String[] tags = selector.tags();
- ServiceReference[] result = new ServiceReference[] {};
- if (tags.length > 0) {
- if (tags[0].equals(filterTags[0])) {
- result = new ServiceReference[] { hcRef };
- }
- }
- return result;
- }
-
- });
-
- Result result = compositeHealthCheck.execute();
-
- verify(healthCheckExecutor, never()).execute(any(HealthCheckSelector.class));
- assertEquals(Result.Status.HEALTH_CHECK_ERROR, result.getStatus());
- }
-
- @Test
- public void testCyclicRecursion() {
-
- // three checks, cyclic
- final String[] filterTags = new String[] { "check2" };
- final DummyHcServiceReference hcRef1 = new DummyHcServiceReference("Check 1", new String[] { "check1" }, filterTags);
- final DummyHcServiceReference hcRef2 = new DummyHcServiceReference("Check 2", new String[] { "check2" }, new String[] { "check3" });
- final DummyHcServiceReference hcRef3 = new DummyHcServiceReference("Check 3", new String[] { "check3" }, new String[] { "check1" });
-
- // test check is hcRef1
- doReturn(hcRef1).when(componentContext).getServiceReference();
- compositeHealthCheck.setFilterTags(filterTags);
-
- compositeHealthCheck.setHealthCheckFilter(new HealthCheckFilter(null) {
-
- @Override
- public ServiceReference[] getHealthCheckServiceReferences(HealthCheckSelector selector, boolean combineTagsWithOr) {
- String[] tags = selector.tags();
- ServiceReference[] result = new ServiceReference[] {};
- if (tags.length > 0) {
- if (tags[0].equals(filterTags[0])) {
- result = new ServiceReference[] { hcRef2 };
- } else if (tags[0].equals("check3")) {
- result = new ServiceReference[] { hcRef3 };
- } else if (tags[0].equals("check1")) {
- result = new ServiceReference[] { hcRef1 };
- }
- }
-
- return result;
- }
-
- });
-
- Result result = compositeHealthCheck.execute();
-
- verify(healthCheckExecutor, never()).execute(any(HealthCheckSelector.class));
- assertEquals(Result.Status.HEALTH_CHECK_ERROR, result.getStatus());
- }
-
- @Test
- public void testCombineWithOr() {
-
- // composite check referencing itself
- final String[] filterTags = new String[] { "check1" };
- compositeHealthCheck.setFilterTags(filterTags);
- compositeHealthCheck.setCombineTagsWithOr(true);
-
- compositeHealthCheck.execute();
-
- verify(healthCheckExecutor, times(1)).execute(argThat(selectorWithTags(filterTags)), argThat(orOptions));
- }
-
- private ArgumentMatcher<HealthCheckExecutionOptions> orOptions = new ArgumentMatcher<HealthCheckExecutionOptions>() {
- @Override
- public boolean matches(HealthCheckExecutionOptions options) {
- return options.isCombineTagsWithOr();
- }
-
- @Override
- public String toString() {
- return "options combining tags with or.";
- }
- };
-
- private ArgumentMatcher<HealthCheckExecutionOptions> andOptions = new ArgumentMatcher<HealthCheckExecutionOptions>() {
- @Override
- public boolean matches(HealthCheckExecutionOptions options) {
- return !options.isCombineTagsWithOr();
- }
-
- @Override
- public String toString() {
- return "options combining tags with and.";
- }
- };
-
- private static class DummyHcServiceReference implements ServiceReference<Object> {
-
- private long id;
- private String name;
- private String[] tags;
- private String[] filterTags;
-
- public DummyHcServiceReference(String name, String[] tags, String[] filterTags) {
- super();
- this.id = (long) (Math.random() * Long.MAX_VALUE);
- this.name = name;
- this.tags = tags;
- this.filterTags = filterTags;
- }
-
- @Override
- public Object getProperty(String key) {
-
- if (Constants.SERVICE_ID.equals(key)) {
- return id;
- } else if (HealthCheck.NAME.equals(key)) {
- return name;
- } else if (HealthCheck.MBEAN_NAME.equals(key)) {
- return name;
- } else if (HealthCheck.TAGS.equals(key)) {
- return tags;
- } else if (CompositeHealthCheck.PROP_FILTER_TAGS.equals(key)) {
- return filterTags;
- } else if (ComponentConstants.COMPONENT_NAME.equals(key)) {
- return filterTags != null ? CompositeHealthCheck.class.getName() : "some.other.HealthCheck";
- } else {
- return null;
- }
- }
-
- @Override
- public String[] getPropertyKeys() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Bundle getBundle() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Bundle[] getUsingBundles() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isAssignableTo(Bundle bundle, String className) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int compareTo(Object reference) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Dictionary getProperties() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Object adapt(Class type) {
- throw new UnsupportedOperationException();
- }
- }
-}
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImplTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImplTest.java
deleted file mode 100644
index faf76a2fc4..0000000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/HealthCheckExecutorImplTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import java.util.Collection;
-import java.util.Date;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.Result.Status;
-import org.apache.felix.hc.api.ResultLog.Entry;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-
-public class HealthCheckExecutorImplTest {
-
- @InjectMocks
- private HealthCheckExecutorImpl healthCheckExecutorImpl = new HealthCheckExecutorImpl();;
-
- @Mock
- private HealthCheckFuture future;
-
- @Mock
- private HealthCheckMetadata healthCheckMetadata;
-
- @Spy
- private HealthCheckResultCache healthCheckResultCache = new HealthCheckResultCache();
-
- @Before
- public void setup() {
- initMocks(this);
-
- when(future.getHealthCheckMetadata()).thenReturn(healthCheckMetadata);
- when(healthCheckMetadata.getTitle()).thenReturn("Test Check");
- when(healthCheckMetadata.getResultCacheTtlInMs()).thenReturn(null);
-
- // 2 sec normal timeout
- healthCheckExecutorImpl.setTimeoutInMs(2000L);
- // 10 sec timeout for critical
- healthCheckExecutorImpl.setLongRunningFutureThresholdForRedMs(10000L);
- }
-
- @Test
- public void testCollectResultsFromFutures() throws Exception {
-
- List<HealthCheckFuture> futures = new LinkedList<HealthCheckFuture>();
- futures.add(future);
- Collection<HealthCheckExecutionResult> results = new TreeSet<HealthCheckExecutionResult>();
-
- when(future.isDone()).thenReturn(true);
- ExecutionResult testResult = new ExecutionResult(healthCheckMetadata, new Result(Result.Status.OK, "test"), 10L);
- when(future.get()).thenReturn(testResult);
-
- healthCheckExecutorImpl.collectResultsFromFutures(futures, results);
-
- verify(future, times(1)).get();
-
- assertEquals(1, results.size());
- assertTrue(results.contains(testResult));
- }
-
- @Test
- public void testCollectResultsFromFuturesTimeout() throws Exception {
-
- // add an earlier result with status ok (that will be shown as part of the log)
- addResultToCache(Status.OK);
-
- List<HealthCheckFuture> futures = new LinkedList<HealthCheckFuture>();
- futures.add(future);
- Set<HealthCheckExecutionResult> results = new TreeSet<HealthCheckExecutionResult>();
-
- when(future.isDone()).thenReturn(false);
- // simulating a future that was created 5sec ago
- when(future.getCreatedTime()).thenReturn(new Date(new Date().getTime() - 1000 * 5));
-
- healthCheckExecutorImpl.collectResultsFromFutures(futures, results);
-
- verify(future, times(0)).get();
-
- assertEquals(1, results.size());
- HealthCheckExecutionResult result = results.iterator().next();
-
- assertEquals(Result.Status.WARN, result.getHealthCheckResult().getStatus());
-
- // 3 because previous result exists and is part of log
- assertEquals(3, getLogEntryCount(result));
- }
-
- @Test
- public void testCollectResultsFromFuturesCriticalTimeout() throws Exception {
-
- List<HealthCheckFuture> futures = new LinkedList<HealthCheckFuture>();
- futures.add(future);
- Set<HealthCheckExecutionResult> results = new TreeSet<HealthCheckExecutionResult>();
-
- when(future.isDone()).thenReturn(false);
-
- // use an old date now (simulating a future that has run for an hour)
- when(future.getCreatedTime()).thenReturn(new Date(new Date().getTime() - 1000 * 60 * 60));
-
- healthCheckExecutorImpl.collectResultsFromFutures(futures, results);
- assertEquals(1, results.size());
- HealthCheckExecutionResult result = results.iterator().next();
-
- verify(future, times(0)).get();
-
- assertEquals(Result.Status.CRITICAL, result.getHealthCheckResult().getStatus());
- assertEquals(1, getLogEntryCount(result));
- }
-
- @Test
- public void testCollectResultsFromFuturesWarnTimeoutWithPreviousCritical() throws Exception {
-
- // an earlier result with critical
- addResultToCache(Status.CRITICAL);
-
- List<HealthCheckFuture> futures = new LinkedList<HealthCheckFuture>();
- futures.add(future);
- Set<HealthCheckExecutionResult> results = new TreeSet<HealthCheckExecutionResult>();
-
- when(future.isDone()).thenReturn(false);
- // simulating a future that was created 5sec ago
- when(future.getCreatedTime()).thenReturn(new Date(new Date().getTime() - 1000 * 5));
-
- healthCheckExecutorImpl.collectResultsFromFutures(futures, results);
- assertEquals(1, results.size());
- HealthCheckExecutionResult result = results.iterator().next();
-
- verify(future, times(0)).get();
-
- // expect CRITICAL because previous result (before timeout) was CRITICAL (and not only WARN)
- assertEquals(Result.Status.CRITICAL, result.getHealthCheckResult().getStatus());
- assertEquals(3, getLogEntryCount(result));
- }
-
- private int getLogEntryCount(HealthCheckExecutionResult result) {
- int logEntryCount = 0;
- final Iterator<Entry> it = result.getHealthCheckResult().iterator();
- while (it.hasNext()) {
- it.next();
- logEntryCount++;
- }
- return logEntryCount;
- }
-
- private void addResultToCache(Status status) {
- healthCheckResultCache.updateWith(new ExecutionResult(healthCheckMetadata, new Result(status, "Status " + status), 1000));
- }
-}
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/HealthCheckResultCacheTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/HealthCheckResultCacheTest.java
deleted file mode 100644
index a385625755..0000000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/HealthCheckResultCacheTest.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.Result.Status;
-import org.apache.felix.hc.api.ResultLog;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-
-public class HealthCheckResultCacheTest {
-
- private static final int HC_TIMEOUT_NOT_SET = -1;
- private static final int DUR_0_MIN = 0;
- private static final int DUR_1_MIN = 60 * 1000;
- private static final int DUR_2_MIN = 2 * DUR_1_MIN;
- private static final int DUR_3_MIN = 3 * DUR_1_MIN;
- private static final int DUR_4_MIN = 4 * DUR_1_MIN;
-
- private HealthCheckResultCache healthCheckResultCache = new HealthCheckResultCache();
-
- @Test
- public void testHealthCheckResultCache() {
-
- HealthCheckMetadata hc1 = setupHealthCheckMetadata(1, HC_TIMEOUT_NOT_SET);
- ExecutionResult executionResult1 = createResult(hc1, Result.Status.OK, DUR_1_MIN);
- healthCheckResultCache.updateWith(executionResult1);
-
- HealthCheckMetadata hc2 = setupHealthCheckMetadata(2, HC_TIMEOUT_NOT_SET);
- ExecutionResult executionResult2 = createResult(hc2, Result.Status.OK, DUR_3_MIN);
- healthCheckResultCache.updateWith(executionResult2);
-
- HealthCheckMetadata hc3 = setupHealthCheckMetadata(3, DUR_4_MIN);
- ExecutionResult executionResult3 = createResult(hc3, Result.Status.OK, DUR_3_MIN);
- healthCheckResultCache.updateWith(executionResult3);
-
- HealthCheckMetadata hc4 = setupHealthCheckMetadata(4, HC_TIMEOUT_NOT_SET);
- // no result for this yet
-
- List<HealthCheckMetadata> hcList = new ArrayList<>(Arrays.asList(hc1, hc2, hc3, hc4));
- List<HealthCheckExecutionResult> results = new ArrayList<HealthCheckExecutionResult>();
-
- healthCheckResultCache.useValidCacheResults(hcList, results, DUR_2_MIN);
-
- assertTrue("result too old, left in hcList for later execution", hcList.contains(hc2));
- assertTrue("no result was added to cache via updateWith()", hcList.contains(hc4));
- assertEquals("values not found in cache are left in hcList", 2, hcList.size());
-
- assertTrue("result one min old, global timeout 2min", results.contains(executionResult1));
- assertFalse("result three min old, global timeout 2min", results.contains(executionResult2));
- assertTrue("result one three old, HC timeout 4min", results.contains(executionResult3));
- assertEquals(2, results.size());
-
- }
-
- @Test
- public void testgetValidCacheResultMissTimedout() {
- // -- test cache miss due to HC TTL
- HealthCheckMetadata hcWithTtl = setupHealthCheckMetadata(1, DUR_1_MIN);
- ExecutionResult executionResult = createResult(hcWithTtl, Result.Status.OK, DUR_2_MIN);
- healthCheckResultCache.updateWith(executionResult);
-
- HealthCheckExecutionResult result = healthCheckResultCache.getValidCacheResult(hcWithTtl, DUR_3_MIN);
- assertNull(result); // even though global timeout would be ok (2min<3min, the hc timeout of 1min invalidates the result)
- }
-
- @Test
- public void testgetValidCacheResultHitTTL() {
- // -- test cache hit due to HC TTL
- HealthCheckMetadata hcWithTtl = setupHealthCheckMetadata(2, DUR_3_MIN);
- ExecutionResult executionResult = createResult(hcWithTtl, Result.Status.OK, DUR_2_MIN);
- healthCheckResultCache.updateWith(executionResult);
-
- HealthCheckExecutionResult result = healthCheckResultCache.getValidCacheResult(hcWithTtl, DUR_1_MIN);
- assertEquals(executionResult, result); // even though global timeout would invalidate this result (1min<2min, the hc timeout of 3min
- // allows the result)
- }
-
- @Test
- public void testgetValidCacheResultHitPermanent() {
- // -- test Long.MAX_VALUE
- HealthCheckMetadata hcWithTtl = setupHealthCheckMetadata(3, Long.MAX_VALUE);
- ExecutionResult executionResult = createResult(hcWithTtl, Result.Status.OK, DUR_4_MIN);
- healthCheckResultCache.updateWith(executionResult);
-
- HealthCheckExecutionResult result = healthCheckResultCache.getValidCacheResult(hcWithTtl, DUR_1_MIN);
- assertEquals(executionResult, result);
- }
-
- @Test
- public void testCreateExecutionResultWithStickyResults() {
-
- HealthCheckMetadata hcWithStickyResultsSet = setupHealthCheckMetadataWithStickyResults(1, 120 /* 2 minutes */);
- ExecutionResult currentResult = createResult(hcWithStickyResultsSet, Result.Status.OK, DUR_0_MIN);
- HealthCheckExecutionResult overallResultWithStickyResults = healthCheckResultCache
- .createExecutionResultWithStickyResults(currentResult);
- assertTrue("Exact same result is expected if no history exists", currentResult == overallResultWithStickyResults);
-
- // add 4 minutes old WARN to cache
- ExecutionResult oldWarnResult = createResult(hcWithStickyResultsSet, Result.Status.WARN, DUR_4_MIN);
- healthCheckResultCache.updateWith(oldWarnResult);
-
- // check that it is not used
- currentResult = createResult(hcWithStickyResultsSet, Result.Status.OK, DUR_0_MIN);
- overallResultWithStickyResults = healthCheckResultCache.createExecutionResultWithStickyResults(currentResult);
- assertTrue("Exact same result is expected if WARN HC Result is too old", currentResult == overallResultWithStickyResults);
-
- // change WARN to 1 minute age
- ExecutionResult warnResult = createResult(hcWithStickyResultsSet, Result.Status.WARN, DUR_1_MIN);
- healthCheckResultCache.updateWith(warnResult);
-
- overallResultWithStickyResults = healthCheckResultCache.createExecutionResultWithStickyResults(currentResult);
- assertTrue("Expect newly created result as sticky result should be taken into account",
- currentResult != overallResultWithStickyResults);
- assertEquals("Expect status to be taken over from old, sticky WARN", Result.Status.WARN,
- overallResultWithStickyResults.getHealthCheckResult().getStatus());
- assertEquals("Expect 4 entries, two each for current and WARN", 4, getLogMsgCount(overallResultWithStickyResults));
-
- // add 1 minutes old CRITICAL to cache
- ExecutionResult oldCriticalResult = createResult(hcWithStickyResultsSet, Result.Status.CRITICAL, DUR_1_MIN);
- healthCheckResultCache.updateWith(oldCriticalResult);
-
- overallResultWithStickyResults = healthCheckResultCache.createExecutionResultWithStickyResults(currentResult);
- assertTrue("Expect newly created result as sticky result should be taken into account",
- currentResult != overallResultWithStickyResults);
- assertEquals("Expect status to be taken over from old, sticky CRITICAL", Result.Status.CRITICAL,
- overallResultWithStickyResults.getHealthCheckResult().getStatus());
- assertEquals("Expect six entries, two each for current, WARN and CRITICAL result", 6,
- getLogMsgCount(overallResultWithStickyResults));
-
- }
-
- private ExecutionResult createResult(HealthCheckMetadata hc, Status status, int minutesAgo) {
- Date finishedAt = new Date(new Date().getTime() - minutesAgo);
- return new ExecutionResult(hc, new Result(status, "result for hc"), finishedAt, 1, false);
- }
-
- private HealthCheckMetadata setupHealthCheckMetadata(long id, long ttl) {
- ServiceReference<?> serviceRef = Mockito.mock(ServiceReference.class);
- doReturn(id).when(serviceRef).getProperty(Constants.SERVICE_ID);
- doReturn(ttl).when(serviceRef).getProperty(HealthCheck.RESULT_CACHE_TTL_IN_MS);
- doReturn("HC id=" + id).when(serviceRef).getProperty(HealthCheck.NAME);
- return new HealthCheckMetadata(serviceRef);
- }
-
- private HealthCheckMetadata setupHealthCheckMetadataWithStickyResults(long id, long nonOkStickyForSec) {
- ServiceReference<?> serviceRef = Mockito.mock(ServiceReference.class);
- doReturn(id).when(serviceRef).getProperty(Constants.SERVICE_ID);
- doReturn(nonOkStickyForSec).when(serviceRef).getProperty(HealthCheck.KEEP_NON_OK_RESULTS_STICKY_FOR_SEC);
- doReturn("HC id=" + id).when(serviceRef).getProperty(HealthCheck.NAME);
- return new HealthCheckMetadata(serviceRef);
- }
-
- @SuppressWarnings("unused")
- private int getLogMsgCount(HealthCheckExecutionResult result) {
- int count = 0;
- for (ResultLog.Entry entry : result.getHealthCheckResult()) {
- count++;
- }
- return count;
- }
-
-}
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/TempUnavailableGracePeriodEvaluatorTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/TempUnavailableGracePeriodEvaluatorTest.java
deleted file mode 100644
index 5621bec444..0000000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/executor/TempUnavailableGracePeriodEvaluatorTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.executor;
-
-import static java.util.Arrays.asList;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import java.util.List;
-
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.osgi.framework.Constants;
-import org.osgi.framework.ServiceReference;
-
-public class TempUnavailableGracePeriodEvaluatorTest {
-
- private TempUnavailableGracePeriodEvaluator tempUnavailableGracePeriodEvaluator;
-
- @Mock
- private ServiceReference<?> hcServiceRef;
-
- @Before
- public void setup() {
- initMocks(this);
- }
-
- @Test
- public void testGracePeriodIsNotExceeded() {
-
- tempUnavailableGracePeriodEvaluator = new TempUnavailableGracePeriodEvaluator(60*1000 /*1 min */);
-
- HealthCheckExecutionResult result = createResult(1, Result.Status.TEMPORARILY_UNAVAILABLE);
-
- List<HealthCheckExecutionResult> results = asList(result, createResult(2, Result.Status.OK), createResult(3, Result.Status.WARN));
-
- tempUnavailableGracePeriodEvaluator.evaluateGracePeriodForTemporarilyUnavailableResults(results);
-
- assertEquals(Result.Status.TEMPORARILY_UNAVAILABLE, results.get(0).getHealthCheckResult().getStatus());
- assertEquals(Result.Status.OK, results.get(1).getHealthCheckResult().getStatus());
- assertEquals(Result.Status.WARN, results.get(2).getHealthCheckResult().getStatus());
- }
-
-
-
- @Test
- public void testGracePeriodIsExceeded() throws InterruptedException {
-
- tempUnavailableGracePeriodEvaluator = new TempUnavailableGracePeriodEvaluator(20 /* 20ms only */);
-
- HealthCheckExecutionResult result = createResult(1, Result.Status.TEMPORARILY_UNAVAILABLE);
-
- Thread.sleep(50 /* 50ms */);
-
- result = createResult(1, Result.Status.TEMPORARILY_UNAVAILABLE);
-
- List<HealthCheckExecutionResult> results = asList(result, createResult(2, Result.Status.OK), createResult(3, Result.Status.WARN));
-
- tempUnavailableGracePeriodEvaluator.evaluateGracePeriodForTemporarilyUnavailableResults(results);
-
- // overall result has to be CRITICAL now
- assertEquals(Result.Status.CRITICAL, results.get(0).getHealthCheckResult().getStatus());
- assertEquals(Result.Status.OK, results.get(1).getHealthCheckResult().getStatus());
- assertEquals(Result.Status.WARN, results.get(2).getHealthCheckResult().getStatus());
-
-
- // one intermediate OK result followed by yet another TEMPORARILY_UNAVAILABLE has to send it back to TEMPORARILY_UNAVAILABLE
- // (not CRITICAL during grace period of 20ms)
- result = createResult(1, Result.Status.OK);
- result = createResult(1, Result.Status.TEMPORARILY_UNAVAILABLE);
- results.set(0, result);
-
- tempUnavailableGracePeriodEvaluator.evaluateGracePeriodForTemporarilyUnavailableResults(results);
- // overall result has to be back to TEMPORARILY_UNAVAILABLE now
- assertEquals(Result.Status.TEMPORARILY_UNAVAILABLE, results.get(0).getHealthCheckResult().getStatus());
- assertEquals(Result.Status.OK, results.get(1).getHealthCheckResult().getStatus());
- assertEquals(Result.Status.WARN, results.get(2).getHealthCheckResult().getStatus());
- }
-
- private HealthCheckExecutionResult createResult(long serviceId, Result.Status status) {
- doReturn(serviceId).when(hcServiceRef).getProperty(Constants.SERVICE_ID);
- HealthCheckExecutionResult result = new ExecutionResult(new HealthCheckMetadata(hcServiceRef), new Result(status, "Result of status "+status), 1);
- tempUnavailableGracePeriodEvaluator.updateTemporarilyUnavailableTimestampWith(result);
- return result;
- }
-
-
-}
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/filter/ServiceUnavailableFilterTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/filter/ServiceUnavailableFilterTest.java
deleted file mode 100644
index a7593ed436..0000000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/filter/ServiceUnavailableFilterTest.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.filter;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.MockitoAnnotations.initMocks;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.net.URLStreamHandler;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-
-public class ServiceUnavailableFilterTest {
-
- @Mock
- BundleContext bundleContext;
-
- @Mock
- Bundle bundle1;
-
- @Mock
- Bundle bundle2;
-
- ServiceUnavailableFilter serviceUnavailableFilter = new ServiceUnavailableFilter();
-
- @Before
- public void setup() {
- initMocks(this);
-
- doReturn(new Bundle[] { bundle1, bundle2 }).when(bundleContext).getBundles();
- doReturn("bundle1").when(bundle1).getSymbolicName();
- doReturn("bundle2").when(bundle2).getSymbolicName();
-
- }
-
- @Test
- public void testGetResponseTextSimple() {
-
- assertEquals("test", serviceUnavailableFilter.getResponseText(bundleContext, "test"));
- }
-
- @Test
- public void testGetResponseTextFromBundle() throws IOException {
-
- mockUrl(bundle1, "/path.html", "testFromFile");
- assertEquals("testFromFile", serviceUnavailableFilter.getResponseText(bundleContext, "classpath:bundle1:/path.html"));
- }
-
- @Test
- public void testGetResponseTextFromBundleErrors() throws IOException {
-
- mockUrl(bundle1, "/path.html", "testFromFile");
-
- assertThat(serviceUnavailableFilter.getResponseText(bundleContext, "classpath:bundle2:/path.html"), containsString("file not found"));
- assertThat(serviceUnavailableFilter.getResponseText(bundleContext, "classpath:bundle3:/path.html"), containsString("bundle not found"));
- }
-
- private void mockUrl(Bundle bundle, String path, String result) throws IOException {
- URLConnection mockConnection = Mockito.mock(URLConnection.class);
- doReturn(new ByteArrayInputStream(result.getBytes())).when(mockConnection).getInputStream();
-
- final URLStreamHandler handler = new URLStreamHandler() {
-
- @Override
- protected URLConnection openConnection(final URL arg0)
- throws IOException {
- return mockConnection;
- }
- };
-
- doReturn(new URL("file:/" + path, "host", 80, "", handler)).when(bundle1).getEntry(path);
- }
-
-}
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
deleted file mode 100644
index 47f69b5e7b..0000000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/monitor/HealthCheckMonitorTest.java
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.monitor;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyList;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.matches;
-import static org.mockito.Mockito.lenient;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoInteractions;
-import static org.mockito.Mockito.when;
-
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-
-import org.apache.felix.hc.api.HealthCheck;
-import org.apache.felix.hc.api.Result;
-import org.apache.felix.hc.api.condition.Healthy;
-import org.apache.felix.hc.api.condition.Unhealthy;
-import org.apache.felix.hc.api.execution.HealthCheckMetadata;
-import org.apache.felix.hc.api.execution.HealthCheckSelector;
-import org.apache.felix.hc.core.impl.executor.ExecutionResult;
-import org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor;
-import org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool;
-import org.apache.felix.hc.core.impl.scheduling.AsyncIntervalJob;
-import org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializer;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Spy;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.junit.MockitoJUnitRunner;
-import org.mockito.stubbing.Answer;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.component.ComponentConstants;
-import org.osgi.service.component.ComponentContext;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-
-@RunWith(MockitoJUnitRunner.class)
-public class HealthCheckMonitorTest {
-
- private static final String TEST_TAG = "test-tag";
- private static final String HC_RESULT_SERIALIZED = "HC result serialized";
-
- @Spy
- @InjectMocks
- private HealthCheckMonitor healthCheckMonitor;
-
- @Mock
- private BundleContext bundleContext;
-
- @Mock
- private ComponentContext componentContext;
-
- @Mock
- private EventAdmin eventAdmin;
-
- @Mock
- private HealthCheckMonitor.Config config;
-
- @Mock
- private HealthCheckExecutorThreadPool healthCheckExecutorThreadPool;
-
- @Mock
- private ExtendedHealthCheckExecutor healthCheckExecutor;
-
- @Mock
- private HealthCheckMetadata healthCheckMetadata;
-
- @Mock
- private ServiceReference<HealthCheck> healthCheckServiceRef;
-
- @Captor
- private ArgumentCaptor<Event> postedEventsCaptor1;
-
- @Captor
- private ArgumentCaptor<Event> postedEventsCaptor2;
-
- @Mock
- private ServiceRegistration<? extends Healthy> healthyRegistration;
-
- @Mock
- private ServiceRegistration<Unhealthy> unhealthyRegistration;
-
- @Mock
- private ResultTxtVerboseSerializer resultTxtVerboseSerializer;
-
- @Before
- public void before() throws ReflectiveOperationException {
-
- for (Method m : HealthCheckMonitor.Config.class.getDeclaredMethods()) {
- when(m.invoke(config)).thenReturn(m.getDefaultValue());
- }
-
- when(config.intervalInSec()).thenReturn(1000L);
- when(config.tags()).thenReturn(new String[] { TEST_TAG });
-
- when(healthCheckMetadata.getServiceReference()).thenReturn(healthCheckServiceRef);
- when(healthCheckMetadata.getTitle()).thenReturn("Test Check");
-
- Dictionary<String,Object> componentProps = new Hashtable<>();
- componentProps.put(ComponentConstants.COMPONENT_ID, 7L);
- when(componentContext.getProperties()).thenReturn(componentProps);
-
- }
-
- @Test
- public void testActivate() throws InvalidSyntaxException {
-
-
- healthCheckMonitor.activate(bundleContext, config, componentContext);
-
- assertTrue(healthCheckMonitor.monitorJob instanceof AsyncIntervalJob);
-
- verify(healthCheckExecutorThreadPool).scheduleAtFixedRate(any(Runnable.class), eq(1000L));
-
- assertEquals("[HealthCheckMonitor tags=[test-tag]/names=[], intervalInSec=1000/cron=]", healthCheckMonitor.toString());
-
- assertEquals(1, healthCheckMonitor.healthStates.size());
- assertEquals("[HealthState tagOrName=test-tag, isTag=true, status=null, isHealthy=false, statusChanged=false]", healthCheckMonitor.healthStates.get(TEST_TAG).toString());
-
- healthCheckMonitor.deactivate();
- assertEquals(0, healthCheckMonitor.healthStates.size());
-
- }
-
- @Test
- public void testRunRegisterMarkerServices() throws InvalidSyntaxException {
-
- when(config.registerHealthyMarkerService()).thenReturn(true);
- when(config.registerUnhealthyMarkerService()).thenReturn(true);
- healthCheckMonitor.activate(bundleContext, config, componentContext);
-
- resetMarkerServicesContext();
-
- setHcResult(Result.Status.OK);
-
- healthCheckMonitor.run();
-
- verify(healthCheckExecutor).execute(HealthCheckSelector.tags(TEST_TAG));
-
- verify(bundleContext).registerService(eq(Healthy.class), eq(HealthState.MARKER_SERVICE_HEALTHY), any());
- verify(bundleContext, never()).registerService(eq(Unhealthy.class), eq(HealthState.MARKER_SERVICE_UNHEALTHY), any());
- verifyNoInteractions(healthyRegistration, unhealthyRegistration);
-
- resetMarkerServicesContext();
- healthCheckMonitor.run();
- // no status change, no interaction
- verifyNoInteractions(bundleContext, healthyRegistration, unhealthyRegistration);
-
- // change, unhealthy should be registered
- resetMarkerServicesContext();
- setHcResult(Result.Status.TEMPORARILY_UNAVAILABLE);
- healthCheckMonitor.run();
-
- verify(bundleContext, never()).registerService(eq(Healthy.class), eq(HealthState.MARKER_SERVICE_HEALTHY), any());
- verify(bundleContext).registerService(eq(Unhealthy.class), eq(HealthState.MARKER_SERVICE_UNHEALTHY), any());
- verify(healthyRegistration).unregister();
- verifyNoInteractions(unhealthyRegistration);
-
- // change, health should be registered
- resetMarkerServicesContext();
- setHcResult(Result.Status.WARN); // WARN is healthy by default config
- healthCheckMonitor.run();
- verify(bundleContext).registerService(eq(Healthy.class), eq(HealthState.MARKER_SERVICE_HEALTHY), any());
- verify(bundleContext, never()).registerService(eq(Unhealthy.class), eq(HealthState.MARKER_SERVICE_UNHEALTHY), any());
- verify(unhealthyRegistration).unregister();
- verifyNoInteractions(healthyRegistration);
- }
-
- private void resetMarkerServicesContext() {
- reset(bundleContext, healthyRegistration, unhealthyRegistration);
- when(bundleContext.registerService(eq(Healthy.class), eq(HealthState.MARKER_SERVICE_HEALTHY), any())).thenReturn((ServiceRegistration<Healthy>) healthyRegistration);
- lenient().when(bundleContext.registerService(eq(Unhealthy.class), eq(HealthState.MARKER_SERVICE_UNHEALTHY), any())).thenReturn(unhealthyRegistration);
- }
-
- @Test
- public void testRunSendEventsStatusChanges() throws InvalidSyntaxException {
-
- when(config.sendEvents()).thenReturn(HealthCheckMonitor.ChangeType.STATUS_CHANGES);
- when(healthCheckServiceRef.getProperty(ComponentConstants.COMPONENT_NAME)).thenReturn("org.apache.felix.TestHealthCheck");
-
- healthCheckMonitor.activate(bundleContext, config, componentContext);
-
- setHcResult(Result.Status.OK);
-
- healthCheckMonitor.run();
-
- verify(healthCheckExecutor).execute(HealthCheckSelector.tags(TEST_TAG));
-
- verify(eventAdmin, times(2)).postEvent(postedEventsCaptor1.capture());
- List<Event> postedEvents = postedEventsCaptor1.getAllValues();
- assertEquals(2, postedEvents.size());
- assertEquals("org/apache/felix/health/tag/test-tag/STATUS_CHANGED", postedEvents.get(0).getTopic());
- assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthState.EVENT_PROP_STATUS));
- assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/STATUS_CHANGED", postedEvents.get(1).getTopic());
-
- reset(eventAdmin);
- // without status change
- healthCheckMonitor.run();
- // no event
- verifyNoInteractions(eventAdmin);
-
- setHcResult(Result.Status.CRITICAL);
- reset(eventAdmin);
- // with status change
- healthCheckMonitor.run();
- verify(eventAdmin, times(2)).postEvent(postedEventsCaptor2.capture());
- postedEvents = postedEventsCaptor2.getAllValues();
- assertEquals(2, postedEvents.size());
- assertEquals("org/apache/felix/health/tag/test-tag/STATUS_CHANGED", postedEvents.get(0).getTopic());
- assertEquals(Result.Status.CRITICAL, postedEvents.get(0).getProperty(HealthState.EVENT_PROP_STATUS));
- assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthState.EVENT_PROP_PREVIOUS_STATUS));
- assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/STATUS_CHANGED", postedEvents.get(1).getTopic());
-
- reset(eventAdmin);
- // without status change
- healthCheckMonitor.run();
- // no event
- verifyNoInteractions(eventAdmin);
-
- }
-
- @Test
- public void testRunSendEventsAll() throws InvalidSyntaxException {
-
- when(config.sendEvents()).thenReturn(HealthCheckMonitor.ChangeType.ALL);
- when(healthCheckServiceRef.getProperty(ComponentConstants.COMPONENT_NAME)).thenReturn("org.apache.felix.TestHealthCheck");
-
- healthCheckMonitor.activate(bundleContext, config, componentContext);
-
- setHcResult(Result.Status.OK);
-
- healthCheckMonitor.run();
-
- verify(healthCheckExecutor).execute(HealthCheckSelector.tags(TEST_TAG));
-
- verify(eventAdmin, times(2)).postEvent(postedEventsCaptor1.capture());
- List<Event> postedEvents = postedEventsCaptor1.getAllValues();
- assertEquals(2, postedEvents.size());
- assertEquals("org/apache/felix/health/tag/test-tag/STATUS_CHANGED", postedEvents.get(0).getTopic());
- assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthState.EVENT_PROP_STATUS));
- assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/STATUS_CHANGED", postedEvents.get(1).getTopic());
-
- reset(eventAdmin);
- // without status change
- healthCheckMonitor.run();
-
- verify(eventAdmin, times(2)).postEvent(postedEventsCaptor2.capture());
- postedEvents = postedEventsCaptor2.getAllValues();
- assertEquals(2, postedEvents.size());
- assertEquals("org/apache/felix/health/tag/test-tag/UPDATED", postedEvents.get(0).getTopic());
- assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthState.EVENT_PROP_STATUS));
- assertEquals(Result.Status.OK, postedEvents.get(0).getProperty(HealthState.EVENT_PROP_PREVIOUS_STATUS));
- assertEquals("org/apache/felix/health/component/org/apache/felix/TestHealthCheck/UPDATED", postedEvents.get(1).getTopic());
- }
-
-
-
- @Test
- public void testRunLogAll() throws InvalidSyntaxException {
-
- prepareLoggingTest(HealthCheckMonitor.ChangeType.ALL);
-
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(true), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(true), matches(".*healthy:true hasChanged:false .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.WARN);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.WARN);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:true hasChanged:false .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.CRITICAL);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:false hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.CRITICAL);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:false hasChanged:false .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(true), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(true), matches(".*healthy:true hasChanged:false .*" + HC_RESULT_SERIALIZED));
-
- }
-
-
-
- @Test
- public void testRunLogStatusChanges() throws InvalidSyntaxException {
-
- prepareLoggingTest(HealthCheckMonitor.ChangeType.STATUS_CHANGES);
-
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(true), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), anyString());
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.WARN);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.WARN);
- healthCheckMonitor.run();
- verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), anyString());
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.CRITICAL);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:false hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.CRITICAL);
- healthCheckMonitor.run();
- verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), anyString());
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(true), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), anyString());
-
- }
-
-
- @Test
- public void testRunLogStatusChangesOrNotOk() throws InvalidSyntaxException {
-
- prepareLoggingTest(HealthCheckMonitor.ChangeType.STATUS_CHANGES_OR_NOT_OK);
-
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(true), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), anyString());
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.WARN);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.WARN);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:true hasChanged:false .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.CRITICAL);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:false hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.CRITICAL);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(false), matches(".*healthy:false hasChanged:false .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor).logResultItem(eq(true), matches(".*healthy:true hasChanged:true .*" + HC_RESULT_SERIALIZED));
-
- reset(healthCheckMonitor);
- setHcResult(Result.Status.OK);
- healthCheckMonitor.run();
- verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), anyString());
-
- }
-
-
- @Test
- public void testRunLogStatusNone() throws InvalidSyntaxException {
-
- prepareLoggingTest(HealthCheckMonitor.ChangeType.NONE);
-
- for (Result.Status status : Arrays.asList(
- Result.Status.OK, Result.Status.OK,
- Result.Status.WARN, Result.Status.WARN,
- Result.Status.CRITICAL, Result.Status.CRITICAL,
- Result.Status.OK, Result.Status.OK)) {
-
- setHcResult(status);
- healthCheckMonitor.run();
- }
-
- // ensure logging is never called for whatever state remains the same or changes
- verify(healthCheckMonitor, never()).logResultItem(anyBoolean(), anyString());
-
- }
-
- private void prepareLoggingTest(HealthCheckMonitor.ChangeType loggingChangeType) throws InvalidSyntaxException {
- when(config.sendEvents()).thenReturn(HealthCheckMonitor.ChangeType.NONE);
- when(config.logResults()).thenReturn(loggingChangeType);
- healthCheckMonitor.activate(bundleContext, config, componentContext);
- healthCheckMonitor.healthStates.put(TEST_TAG, new HealthState(healthCheckMonitor, TEST_TAG, true));
-
- when(resultTxtVerboseSerializer.serialize(any(String.class), anyList(), eq(false))).thenAnswer(new Answer<String>() {
- @Override
- public String answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- return (String) args[0] + " " + HC_RESULT_SERIALIZED;
- }
- });
- }
-
- private void setHcResult(Result.Status status) {
- when(healthCheckExecutor.execute(HealthCheckSelector.tags(TEST_TAG)))
- .thenReturn(Arrays.asList(new ExecutionResult(healthCheckMetadata, new Result(status, status.toString()), 1)));
- }
-}
diff --git a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParserTest.java b/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParserTest.java
deleted file mode 100644
index d7b7a00b7a..0000000000
--- a/healthcheck/core/src/test/java/org/apache/felix/hc/core/impl/scheduling/cron/embedded/EmbeddedCronParserTest.java
+++ /dev/null
@@ -1,944 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The SF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.apache.felix.hc.core.impl.scheduling.cron.embedded;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.TimeZone;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class EmbeddedCronParserTest {
-
- private final Calendar calendar = new GregorianCalendar();
-
- private final Date date;
-
- private final TimeZone timeZone;
-
- public EmbeddedCronParserTest(final Date date, final TimeZone timeZone) {
- this.date = date;
- this.timeZone = timeZone;
- }
-
- @Parameters(name = "date [{0}], time zone [{1}]")
- public static List<Object[]> getParameters() {
- final List<Object[]> list = new ArrayList<>();
- list.add(new Object[] { new Date(), TimeZone.getTimeZone("PST") });
- list.add(new Object[] { new Date(), TimeZone.getTimeZone("CET") });
- return list;
- }
-
- private static void roundup(final Calendar calendar) {
- calendar.add(Calendar.SECOND, 1);
- calendar.set(Calendar.MILLISECOND, 0);
- }
-
- @Before
- public void setUp() {
- calendar.setTimeZone(timeZone);
- calendar.setTime(date);
- roundup(calendar);
- }
-
- @Test
- public void testAt0And15And45Seconds() {
-
- final EmbeddedCronParser trigger = new EmbeddedCronParser("*/15 * 1-4 * * *", timeZone);
-
- calendar.set(Calendar.DAY_OF_MONTH, 2);
- calendar.set(Calendar.HOUR_OF_DAY, 9);
- calendar.set(Calendar.MINUTE, 53);
- calendar.set(Calendar.SECOND, 50);
-
- Date intialTime = calendar.getTime();
- calendar.set(Calendar.DAY_OF_MONTH, 3);
- calendar.set(Calendar.HOUR_OF_DAY, 1);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTime().getTime(), trigger.next(intialTime.getTime()));
-
- intialTime = calendar.getTime();
- calendar.set(Calendar.SECOND, 15);
- assertEquals(calendar.getTime().getTime(), trigger.next(intialTime.getTime()));
-
- intialTime = calendar.getTime();
- calendar.set(Calendar.SECOND, 30);
- assertEquals(calendar.getTime().getTime(), trigger.next(intialTime.getTime()));
-
- intialTime = calendar.getTime();
- calendar.set(Calendar.SECOND, 45);
- assertEquals(calendar.getTime().getTime(), trigger.next(intialTime.getTime()));
-
- }
-
-
- @Test
- public void testAt0Minutes() {
-
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 */2 1-4 * * *", timeZone);
-
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 9);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
-
- Date intialTime = calendar.getTime();
- calendar.set(Calendar.DAY_OF_MONTH, 2);
- calendar.set(Calendar.HOUR_OF_DAY, 1);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- long next = trigger.next(intialTime.getTime());
- assertEquals(calendar.getTime().getTime(), next);
-
-
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testWith0Increment() {
-
- new EmbeddedCronParser("*/0 * * * * *").next(calendar.getTime().getTime());
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void testWithNegativeIncrement() {
- new EmbeddedCronParser("*/-1 * * * * *").next(calendar.getTime().getTime());
- }
-
- @Test
- public void testMatchAll() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * *", timeZone);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testMatchLastSecond() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 58);
- assertMatchesNextSecond(trigger, calendar);
- }
-
- @Test
- public void testMatchSpecificSecond() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("10 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 9);
- assertMatchesNextSecond(trigger, calendar);
- }
-
- @Test
- public void testIncrementSecondByOne() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("11 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 10);
- final Date date = calendar.getTime();
- calendar.add(Calendar.SECOND, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementSecondWithPreviousExecutionTooEarly() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("11 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 11);
- final Date date = calendar.getTime();
-
- calendar.add(Calendar.MINUTE, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementSecondAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("10 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 11);
- final Date date = calendar.getTime();
- calendar.add(Calendar.SECOND, 59);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testSecondRange() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("10-15 * * * * *", timeZone);
- calendar.set(Calendar.SECOND, 9);
- assertMatchesNextSecond(trigger, calendar);
- calendar.set(Calendar.SECOND, 14);
- assertMatchesNextSecond(trigger, calendar);
- }
-
- @Test
- public void testIncrementMinute() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 * * * * *", timeZone);
- calendar.set(Calendar.MINUTE, 10);
- Date date = calendar.getTime();
- calendar.add(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- calendar.add(Calendar.MINUTE, 1);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- }
-
- @Test
- public void testIncrementMinuteByOne() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 11 * * * *", timeZone);
- calendar.set(Calendar.MINUTE, 10);
- final Date date = calendar.getTime();
-
- calendar.add(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementMinuteAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 10 * * * *", timeZone);
- calendar.set(Calendar.MINUTE, 11);
- calendar.set(Calendar.SECOND, 0);
- final Date date = calendar.getTime();
- calendar.add(Calendar.MINUTE, 59);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementHour() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 * * * *", timeZone);
- calendar.set(Calendar.MONTH, 9);
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- calendar.set(Calendar.HOUR_OF_DAY, 11);
- calendar.set(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- Date date = calendar.getTime();
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.HOUR_OF_DAY, 12);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- calendar.set(Calendar.HOUR_OF_DAY, 13);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementHourAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 * * * *", timeZone);
- calendar.set(Calendar.MONTH, 9);
- calendar.set(Calendar.DAY_OF_MONTH, 10);
- calendar.set(Calendar.HOUR_OF_DAY, 23);
- calendar.set(Calendar.MINUTE, 1);
- calendar.set(Calendar.SECOND, 0);
- Date date = calendar.getTime();
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.DAY_OF_MONTH, 11);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- calendar.set(Calendar.HOUR_OF_DAY, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementDayOfMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- Date date = calendar.getTime();
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- assertEquals(2, calendar.get(Calendar.DAY_OF_MONTH));
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
- assertEquals(3, calendar.get(Calendar.DAY_OF_MONTH));
- }
-
- @Test
- public void testIncrementDayOfMonthByOne() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * 10 * *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 9);
- final Date date = calendar.getTime();
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementDayOfMonthAndRollover() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("* * * 10 * *", timeZone);
- calendar.set(Calendar.DAY_OF_MONTH, 11);
- final Date date = calendar.getTime();
- calendar.add(Calendar.MONTH, 1);
- calendar.set(Calendar.DAY_OF_MONTH, 10);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testDailyTriggerInShortMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
- calendar.set(Calendar.MONTH, 8); // September: 30 days
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- Date date = calendar.getTime();
- calendar.set(Calendar.MONTH, 9); // October
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.DAY_OF_MONTH, 1);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.set(Calendar.DAY_OF_MONTH, 2);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testDailyTriggerInLongMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
- calendar.set(Calendar.MONTH, 7); // August: 31 days and not a daylight saving boundary
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- Date date = calendar.getTime();
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.DAY_OF_MONTH, 31);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.set(Calendar.MONTH, 8); // September
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testDailyTriggerOnDaylightSavingBoundary() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 * * *", timeZone);
- calendar.set(Calendar.MONTH, 9); // October: 31 days and a daylight saving boundary in CET
- calendar.set(Calendar.DAY_OF_MONTH, 30);
- Date date = calendar.getTime();
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- calendar.set(Calendar.DAY_OF_MONTH, 31);
-
- date = new Date(trigger.next(date.getTime()));
- assertEquals(calendar.getTimeInMillis(), date.getTime());
-
- calendar.set(Calendar.MONTH, 10); // November
- calendar.set(Calendar.DAY_OF_MONTH, 1);
- assertEquals(calendar.getTimeInMillis(), trigger.next(date.getTime()));
- }
-
- @Test
- public void testIncrementMonth() {
- final EmbeddedCronParser trigger = new EmbeddedCronParser("0 0 0 1 * *", timeZone);
- calendar.set(Calendar.MONTH, 9);
... 3880 lines suppressed ...