This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.hc.api-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-hc-api.git
commit 89fda576fbe142420a6559a2a5a32c43c7dfa7b7 Author: Justin Edelson <[email protected]> AuthorDate: Mon Apr 10 17:13:25 2017 +0000 SLING-6773 - separate HC API from Core git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/healthcheck/api@1790879 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 86 ++++++++++ .../java/org/apache/sling/hc/api/HealthCheck.java | 83 ++++++++++ src/main/java/org/apache/sling/hc/api/Result.java | 81 ++++++++++ .../java/org/apache/sling/hc/api/ResultLog.java | 124 +++++++++++++++ .../api/execution/HealthCheckExecutionOptions.java | 110 +++++++++++++ .../api/execution/HealthCheckExecutionResult.java | 65 ++++++++ .../hc/api/execution/HealthCheckExecutor.java | 53 ++++++ .../sling/hc/api/execution/package-info.java | 24 +++ .../java/org/apache/sling/hc/api/package-info.java | 24 +++ .../apache/sling/hc/util/FormattingResultLog.java | 80 ++++++++++ .../apache/sling/hc/util/HealthCheckFilter.java | 160 +++++++++++++++++++ .../apache/sling/hc/util/HealthCheckMetadata.java | 177 +++++++++++++++++++++ .../sling/hc/util/SimpleConstraintChecker.java | 80 ++++++++++ .../org/apache/sling/hc/util/package-info.java | 24 +++ .../org/apache/sling/hc/api/ResultLogTest.java | 107 +++++++++++++ .../java/org/apache/sling/hc/api/ResultTest.java | 65 ++++++++ .../sling/hc/util/SimpleConstraintCheckerTest.java | 167 +++++++++++++++++++ 17 files changed, 1510 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..35ad1bb --- /dev/null +++ b/pom.xml @@ -0,0 +1,86 @@ +<?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/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>org.apache.sling.hc.reactor</artifactId> + <groupId>org.apache.sling</groupId> + <version>1.0.3-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.hc.api</artifactId> + <packaging>bundle</packaging> + <version>0.0.99-SNAPSHOT</version> + + <name>Apache Sling Health Check API</name> + <inceptionYear>2013</inceptionYear> + + <description> + The Sling Health Check API + </description> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/healthcheck/api</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/healthcheck/api</developerConnection> + <url>http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/healthcheck/api</url> + </scm> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.osgi</artifactId> + <version>2.2.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.5</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.6.2</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + </dependencies> + +</project> \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/api/HealthCheck.java b/src/main/java/org/apache/sling/hc/api/HealthCheck.java new file mode 100644 index 0000000..2fd269a --- /dev/null +++ b/src/main/java/org/apache/sling/hc/api/HealthCheck.java @@ -0,0 +1,83 @@ +/* + * 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.sling.hc.api; + +import aQute.bnd.annotation.ConsumerType; + + +/** + * Health Check services can be executed and + * return an execution {@link Result}. + * + * Clients should not look up health checks directly but + * rather use the {@link org.apache.sling.hc.api.execution.HealthCheckExecutor} + * service and executed checks based on tags. + * + * If the {@link #MBEAN_NAME} service registration property is set, + * the health check is registered as an mbean and can be invoked + * by getting the MBean from the JMX registry. + */ +@ConsumerType +public interface HealthCheck { + + /** + * Optional service property: the name of a health check. + * This name should be unique, however there might be more than one + * health check service with the same value for this property. + * The value of this property must be of type String. + */ + String NAME = "hc.name"; + + /** + * Optional service property: the name of the MBean for registering + * the health check as an MBean. If this property is missing the + * health check is not registered as a JMX MBean. + * If there is more than one service with the same value for this + * property, the one with the highest service ranking is registered + * only. + * The value of this property must be of type String. + */ + String MBEAN_NAME = "hc.mbean.name"; + + /** + * Optional service property: tags for categorizing the health check + * services. + * The value of this property must be of type String or String array. + */ + String TAGS = "hc.tags"; + + /** + * Optional service property: If this property is set the health check + * will be executed asynchronously using the cron expression provided. + */ + String ASYNC_CRON_EXPRESSION = "hc.async.cronExpression"; + + /** + * Optional service property: TTL for health check {@link Result}. + * The value of this property must be of type {@link Long} and is + * specified in ms. + */ + String RESULT_CACHE_TTL_IN_MS = "hc.resultCacheTtlInMs"; + + /** + * 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. + */ + Result execute(); +} diff --git a/src/main/java/org/apache/sling/hc/api/Result.java b/src/main/java/org/apache/sling/hc/api/Result.java new file mode 100644 index 0000000..85e4153 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/api/Result.java @@ -0,0 +1,81 @@ +/* + * 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.sling.hc.api; + +import java.util.Iterator; + +/** The result of executing a {@link HealthCheck} */ +public class Result implements Iterable <ResultLog.Entry> { + + protected final ResultLog resultLog; + + public enum Status { + DEBUG, // used by ResultLog for debug messages, not an actual output status + INFO, // used by ResultLog for info messages, not an actual output status + OK, // no problem + WARN, // health check detected something wrong but not critical + CRITICAL, // health check detected a critical problem + HEALTH_CHECK_ERROR // health check did not execute properly + } + + /** Build a single-value Result + * @param s if lower than OK, our status is set to OK */ + public Result(final Status s, final String explanation) { + resultLog = new ResultLog().add(new ResultLog.Entry(s, explanation)); + } + + /** + * Build a single-value Result with exception + * + * @param s if lower than OK, our status is set to OK + */ + public Result(final Status s, final String explanation, final Exception e) { + resultLog = new ResultLog().add(new ResultLog.Entry(s, explanation, e)); + } + + /** Build a a Result based on a ResultLog, which can provide + * more details than a single-value Result. + */ + public Result(final ResultLog log) { + resultLog = new ResultLog(log); + } + + /** True if our status is OK - provides a convenient way of + * checking that. + */ + public boolean isOk() { + return getStatus().equals(Status.OK); + } + + /** Return our Status */ + public Status getStatus() { + return resultLog.getAggregateStatus(); + } + + /** Return an Iterator on the entries of our ResultLog */ + @Override + public Iterator<ResultLog.Entry> iterator() { + return resultLog.iterator(); + } + + @Override + public String toString() { + return "Result [status=" + getStatus() + ", resultLog=" + resultLog + "]"; + } + +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/api/ResultLog.java b/src/main/java/org/apache/sling/hc/api/ResultLog.java new file mode 100644 index 0000000..a5f1d0e --- /dev/null +++ b/src/main/java/org/apache/sling/hc/api/ResultLog.java @@ -0,0 +1,124 @@ +/* + * 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.sling.hc.api; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.apache.sling.hc.api.Result.Status; + +/** The log of a Result, allows for providing multiple lines + * of information which are aggregated as a single Result. */ +public class ResultLog implements Iterable<ResultLog.Entry> { + + private List<Entry> entries = new LinkedList<Entry>(); + private Status aggregateStatus; + + /** An entry in this log */ + public static class Entry { + private final Status status; + private final String message; + private final Exception exception; + + public Entry(Status s, String message) { + this(s, message, null); + } + + public Entry(Status s, String message, Exception exception) { + this.status = s; + this.message = message; + this.exception = exception; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(status.toString()).append(" ").append(message); + if (exception != null) { + builder.append(" Exception: " + exception.getMessage()); + } + return builder.toString(); + } + + public Status getStatus() { + return status; + } + + public String getMessage() { + return message; + } + + public Exception getException() { + return exception; + } + + } + + /** Build a log. Initial aggregate status is + * set to WARN, as an empty log is not considered ok. + * That's reset to OK before adding the first log entry, + * and then the status aggregation rules take over. + */ + public ResultLog() { + aggregateStatus = Result.Status.WARN; + } + + /** + * Create a copy of the result log + */ + public ResultLog(final ResultLog log) { + this.aggregateStatus = log.aggregateStatus; + this.entries = new ArrayList<ResultLog.Entry>(log.entries); + } + + /** Add an entry to this log. The aggregate status of + * this is set to the highest of the current aggregate status + * and the new Entry's status */ + public ResultLog add(Entry e) { + if(entries.isEmpty()) { + aggregateStatus = Result.Status.OK; + } + entries.add(e); + if(e.getStatus().ordinal() > aggregateStatus.ordinal()) { + aggregateStatus = e.getStatus(); + } + return this; + } + + /** Return an Iterator on our entries */ + @Override + public Iterator<ResultLog.Entry> iterator() { + return entries.iterator(); + } + + /** Return our aggregate status, i.e. the highest status + * of the entries added to this log. Starts at OK for an + * empty ResultLog, so cannot be lower than that. + */ + public Status getAggregateStatus() { + return aggregateStatus; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ResultLog: "); + sb.append(this.entries.toString()); + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionOptions.java b/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionOptions.java new file mode 100644 index 0000000..460323d --- /dev/null +++ b/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionOptions.java @@ -0,0 +1,110 @@ +/* + * 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.sling.hc.api.execution; + + +/** Options for behavior of health check execution. */ +public class HealthCheckExecutionOptions { + + private boolean forceInstantExecution = false; + private boolean combineTagsWithOr = false; + private int overrideGlobalTimeout = 0; + + @Override + public String toString() { + return "[HealthCheckExecutionOptions forceInstantExecution=" + forceInstantExecution + ", combineTagsWithOr=" + combineTagsWithOr + + ", overrideGlobalTimeout=" + overrideGlobalTimeout + "]"; + } + + /** + * If activated, this will ensure that asynchronous checks will be executed immediately. + * + * @param forceInstantExecution boolean flag + */ + public void setForceInstantExecution(boolean forceInstantExecution) { + this.forceInstantExecution = forceInstantExecution; + } + + /** + * If activated, the given tags will be combined with a logical "or" instead of "and". + * + * @param combineTagsWithOr boolean flag + */ + public void setCombineTagsWithOr(boolean combineTagsWithOr) { + this.combineTagsWithOr = combineTagsWithOr; + } + + /** + * Allows to override the global timeout for this particular execution of the health check. + * + * @param overrideGlobalTimeout timeout in ms to be used for this execution of the execution + */ + public void setOverrideGlobalTimeout(int overrideGlobalTimeout) { + this.overrideGlobalTimeout = overrideGlobalTimeout; + } + + /** + * @return true if instant execution is turned on + */ + public boolean isForceInstantExecution() { + return forceInstantExecution; + } + + /** + * @return true if combining tags with or is turned on + */ + public boolean isCombineTagsWithOr() { + return combineTagsWithOr; + } + + /** + * @return the timeout to be used for this execution (overriding the global timeout) + */ + public int getOverrideGlobalTimeout() { + return overrideGlobalTimeout; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (combineTagsWithOr ? 1231 : 1237); + result = prime * result + (forceInstantExecution ? 1231 : 1237); + result = prime * result + overrideGlobalTimeout; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + HealthCheckExecutionOptions other = (HealthCheckExecutionOptions) obj; + if (combineTagsWithOr != other.combineTagsWithOr) + return false; + if (forceInstantExecution != other.forceInstantExecution) + return false; + if (overrideGlobalTimeout != other.overrideGlobalTimeout) + return false; + return true; + } + +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionResult.java b/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionResult.java new file mode 100644 index 0000000..8a09920 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutionResult.java @@ -0,0 +1,65 @@ +/* + * 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.sling.hc.api.execution; + +import java.util.Date; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.util.HealthCheckMetadata; + +import aQute.bnd.annotation.ProviderType; + +/** + * Interface for health check executions via the {@link HealthCheckExecutor}. + * + * If the execution of the health check timed out, the method + * + */ +@ProviderType +public interface HealthCheckExecutionResult { + + /** + * Get the result of the health check run. + */ + Result getHealthCheckResult(); + + /** + * Get the elapsed time in ms + */ + long getElapsedTimeInMs(); + + /** + * Get the date, the health check finished or if the + * execution timed out, the execution was aborted. + * @return The finished date of the execution. + */ + Date getFinishedAt(); + + /** + * Returns true if the execution has timed out. In this + * case the result does not reflect the real result of the + * underlying check, but a result indicating the timeout. + * @return <code>true</code> if execution timed out. + */ + boolean hasTimedOut(); + + /** + * Get the meta data about the health check service + */ + HealthCheckMetadata getHealthCheckMetadata(); +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutor.java b/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutor.java new file mode 100644 index 0000000..e26843f --- /dev/null +++ b/src/main/java/org/apache/sling/hc/api/execution/HealthCheckExecutor.java @@ -0,0 +1,53 @@ +/* + * 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.sling.hc.api.execution; + +import java.util.List; + +import org.apache.sling.hc.api.HealthCheck; + +import aQute.bnd.annotation.ProviderType; + +/** + * Executes health checks registered as OSGi services and + * implementing the interface {@link HealthCheck}. + * + */ +@ProviderType +public interface HealthCheckExecutor { + + /** + * Executes all health checks with the supplied list of tags. + * If no tags are supplied, all health checks are executed. + * + * @return List of results. The list might be empty. + */ + List<HealthCheckExecutionResult> execute(String... tags); + + /** + * Executes all health checks with the supplied list of tags. + * If no tags are supplied, all health checks are executed. + * + * @param options options for controlling execution behavior + * @param tags tags to be executed + * + * @return List of results. The list might be empty. + */ + List<HealthCheckExecutionResult> execute(HealthCheckExecutionOptions options, String... tags); + +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/api/execution/package-info.java b/src/main/java/org/apache/sling/hc/api/execution/package-info.java new file mode 100644 index 0000000..26383c4 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/api/execution/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +@Version("1.1.0") +package org.apache.sling.hc.api.execution; + +import aQute.bnd.annotation.Version; + diff --git a/src/main/java/org/apache/sling/hc/api/package-info.java b/src/main/java/org/apache/sling/hc/api/package-info.java new file mode 100644 index 0000000..659caa4 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/api/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +@Version("1.1.0") +package org.apache.sling.hc.api; + +import aQute.bnd.annotation.Version; + diff --git a/src/main/java/org/apache/sling/hc/util/FormattingResultLog.java b/src/main/java/org/apache/sling/hc/util/FormattingResultLog.java new file mode 100644 index 0000000..e37dd72 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/util/FormattingResultLog.java @@ -0,0 +1,80 @@ +/* + * 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.sling.hc.util; + +import java.text.NumberFormat; +import java.util.Locale; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLog; +import org.slf4j.helpers.MessageFormatter; + +/** Utility that provides a logging-like facade on a ResultLog */ +public class FormattingResultLog extends ResultLog { + + private ResultLog.Entry createEntry(Result.Status status, String format, Object ... args) { + return new ResultLog.Entry(status, MessageFormatter.arrayFormat(format, args).getMessage()); + } + + public void debug(String format, Object ... args) { + add(createEntry(Result.Status.DEBUG, format, args)); + } + + public void info(String format, Object ... args) { + add(createEntry(Result.Status.INFO, format, args)); + } + + public void warn(String format, Object ... args) { + add(createEntry(Result.Status.WARN, format, args)); + } + + public void critical(String format, Object ... args) { + add(createEntry(Result.Status.CRITICAL, format, args)); + } + + public void healthCheckError(String format, Object ... args) { + add(createEntry(Result.Status.HEALTH_CHECK_ERROR, format, args)); + } + + /** Utility method to return any magnitude of milliseconds in a human readable format using the appropriate time unit (ms, sec, min) depending on the + * magnitude of the input. + * + * @param millis milliseconds + * @return a string with a number and a unit */ + public static String msHumanReadable(final long millis) { + + double number = millis; + final String[] units = new String[] { "ms", "sec", "min", "h", "days" }; + final double[] divisors = new double[] { 1000, 60, 60, 24 }; + + int magnitude = 0; + do { + double currentDivisor = divisors[Math.min(magnitude, divisors.length - 1)]; + if (number < currentDivisor) { + break; + } + number /= currentDivisor; + magnitude++; + } while (magnitude < units.length - 1); + NumberFormat format = NumberFormat.getNumberInstance(Locale.UK); + format.setMinimumFractionDigits(0); + format.setMaximumFractionDigits(1); + String result = format.format(number) + units[magnitude]; + return result; + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/util/HealthCheckFilter.java b/src/main/java/org/apache/sling/hc/util/HealthCheckFilter.java new file mode 100644 index 0000000..5466492 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/util/HealthCheckFilter.java @@ -0,0 +1,160 @@ +/* + * 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.sling.hc.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.sling.hc.api.HealthCheck; +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. + * Once this filter object and the returned health check services are no longer + * be used {@link #dispose()} should be called, to free the service + * references. + * + * 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()); + private final BundleContext bundleContext; + + + public static final String OMIT_PREFIX = "-"; + + private final Set<ServiceReference> usedReferences = new HashSet<ServiceReference>(); + + /** + * Create a new filter object + */ + public HealthCheckFilter(final BundleContext bc) { + bundleContext = bc; + } + + /** + * Get all health check services with one of the supplied tags. + * @return A list of services - might be the empty list if none matches + */ + @SuppressWarnings("unchecked") + public List<HealthCheck> getTaggedHealthChecks(final String... tags) { + final ServiceReference [] refs = this.getTaggedHealthCheckServiceReferences(tags); + final List<HealthCheck> result = new ArrayList<HealthCheck>(); + + if ( refs != null ) { + final List<ServiceReference> sortedRefs = Arrays.asList(refs); + Collections.sort(sortedRefs); + + for(final ServiceReference ref : sortedRefs) { + final HealthCheck hc = (HealthCheck)bundleContext.getService(ref); + log.debug("Selected HealthCheck service {}", hc); + if ( hc != null ) { + this.usedReferences.add(ref); + result.add(hc); + } + } + } + + return result; + } + + /** + * Get all service references for health check services with one of the supplied tags. Uses logical "and" to combine tags. + * @return An array of service references - might be an empty error if none matches + */ + public ServiceReference[] getTaggedHealthCheckServiceReferences(final String... tags) { + return getTaggedHealthCheckServiceReferences(false, tags); + } + + /** + * Get all service references for health check services with one of the supplied tags. + * + * @param combineWithOr If true will return all health checks that have at least one of the tags set. + * If false will return only health checks that have all given tags assigned. + * @param tags the tags to look for + * @return An array of service references - might be an empty error if none matches + */ + public ServiceReference[] getTaggedHealthCheckServiceReferences(boolean combineWithOr, final String... tags) { + // Build service filter + final StringBuilder filterBuilder = new StringBuilder(); + filterBuilder.append("(&(objectClass=").append(HealthCheck.class.getName()).append(")"); + final int prefixLen = OMIT_PREFIX.length(); + final StringBuilder filterBuilderForOrOperator = new StringBuilder(); // or filters + for(String tag : tags) { + tag = tag.trim(); + if(tag.length() == 0) { + continue; + } + if(tag.startsWith(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 (combineWithOr) { + filterBuilderForOrOperator.append("(").append(HealthCheck.TAGS).append("=").append(tag).append(")"); + } else { + filterBuilder.append("(").append(HealthCheck.TAGS).append("=").append(tag).append(")"); + } + } + } + // add "or" clause if we have accumulated any + if (filterBuilderForOrOperator.length() > 0) { + filterBuilder.append("(|").append(filterBuilderForOrOperator).append(")"); + } + filterBuilder.append(")"); + + log.debug("OSGi service filter in getTaggedHealthCheckServiceReferences(): {}", filterBuilder); + + try { + final String filterString = filterBuilder.length() == 0 ? null : filterBuilder.toString(); + bundleContext.createFilter(filterString); // check syntax early + final ServiceReference[] refs = bundleContext.getServiceReferences(HealthCheck.class.getName(), filterString); + if (refs == null) { + log.debug("Found no HealthCheck services with filter [{}]", filterString); + return new ServiceReference[0]; + } else { + log.debug("Found {} HealthCheck services with 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]; + } + } + + /** + * Dispose all used service references + */ + public void dispose() { + for(final ServiceReference ref : this.usedReferences) { + this.bundleContext.ungetService(ref); + } + this.usedReferences.clear(); + } +} diff --git a/src/main/java/org/apache/sling/hc/util/HealthCheckMetadata.java b/src/main/java/org/apache/sling/hc/util/HealthCheckMetadata.java new file mode 100644 index 0000000..f5fdcb5 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/util/HealthCheckMetadata.java @@ -0,0 +1,177 @@ +/* + * 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.sling.hc.util; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.apache.sling.hc.api.HealthCheck; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceReference; + +/** + * This class helps retrieving meta data information about a health check service. + * @since 1.1 + */ +public class HealthCheckMetadata { + + private final String name; + + private final String mbeanName; + + private final String title; + + private final long serviceId; + + private final List<String> tags; + + private final String asyncCronExpression; + + private final transient ServiceReference serviceReference; + + private final Long resultCacheTtlInMs; + + public HealthCheckMetadata(final ServiceReference ref) { + this.serviceId = (Long) ref.getProperty(Constants.SERVICE_ID); + this.name = (String) ref.getProperty(HealthCheck.NAME); + this.mbeanName = (String) ref.getProperty(HealthCheck.MBEAN_NAME); + this.title = getHealthCheckTitle(ref); + this.tags = arrayPropertyToListOfStr(ref.getProperty(HealthCheck.TAGS)); + this.asyncCronExpression = (String) ref.getProperty(HealthCheck.ASYNC_CRON_EXPRESSION); + this.resultCacheTtlInMs = (Long)ref.getProperty(HealthCheck.RESULT_CACHE_TTL_IN_MS); + this.serviceReference = ref; + } + + /** + * The name of the health check as defined through the {@link HealthCheck#NAME} + * property. + * @return The name or <code>null</code> + */ + public String getName() { + return name; + } + + /** + * The mbean name of the health check as defined through the {@link HealthCheck#MBEAN_NAME} + * property. + * @return The mbean name or <code>null</code> + */ + public String getMBeanName() { + return mbeanName; + } + + /** + * The title of the health check. + * If the health check has a name, this is used as the title. + * Otherwise the description, PID and service ID are checked + * for values. + */ + public String getTitle() { + return title; + } + + /** + * Return the list of defined tags for this check as set + * through {@link HealthCheckMetadata#tags} + * @return + */ + public List<String> getTags() { + return tags; + } + + + /** + * Return the cron expression used for asynchronous execution. + */ + public String getAsyncCronExpression() { + return asyncCronExpression; + } + + /** + * Return the service id. + */ + public long getServiceId() { + return this.serviceId; + } + + /** + * Get the service reference. + */ + public ServiceReference getServiceReference() { + return this.serviceReference; + } + + /** + * TTL for the result cache in ms. + * + * @return TTL for the result cache or <code>null</code> if not configured. + */ + public Long getResultCacheTtlInMs() { + return resultCacheTtlInMs; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (serviceId ^ (serviceId >>> 32)); + return result; + } + + @Override + public boolean equals(final Object obj) { + if ( !(obj instanceof HealthCheckMetadata)) { + return false; + } + final HealthCheckMetadata other = (HealthCheckMetadata) obj; + return serviceId == other.serviceId; + } + + @Override + public String toString() { + return "HealthCheck '" + name + "'"; + } + + private String getHealthCheckTitle(final ServiceReference ref) { + String name = (String) ref.getProperty(HealthCheck.NAME); + if (StringUtils.isBlank(name)) { + name = PropertiesUtil.toString(ref.getProperty(Constants.SERVICE_DESCRIPTION), null); + } + if (StringUtils.isBlank(name)) { + name = "HealthCheck:" + ref.getProperty(Constants.SERVICE_ID); + final String pid = PropertiesUtil.toString(ref.getProperty(Constants.SERVICE_PID), null); + if ( !StringUtils.isBlank(pid) ) { + name = name + " (" + pid + ")"; + } + } + return name; + } + + private List<String> arrayPropertyToListOfStr(final Object arrayProp) { + List<String> res = new LinkedList<String>(); + if (arrayProp instanceof String) { + res.add((String) arrayProp); + } else if (arrayProp instanceof String[]) { + res.addAll(Arrays.asList((String[]) arrayProp)); + } + return res; + } +} diff --git a/src/main/java/org/apache/sling/hc/util/SimpleConstraintChecker.java b/src/main/java/org/apache/sling/hc/util/SimpleConstraintChecker.java new file mode 100644 index 0000000..b506840 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/util/SimpleConstraintChecker.java @@ -0,0 +1,80 @@ +/* + * 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.sling.hc.util; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLog; + +/** Simple check of numeric values against expressions + * like < N, > N, between two values etc. + * See the SimpleConstraintCheckerTest for examples. + */ +public class SimpleConstraintChecker { + + public static final String CONTAINS = "contains"; + + /** Check value against expression and report to result */ + public void check(Object inputValue, String constraint, ResultLog resultLog) { + + final String stringValue = inputValue == null ? "" : inputValue.toString(); + + if(constraint == null || constraint.trim().length() == 0) { + throw new IllegalArgumentException("Empty constraint, cannot evaluate"); + } + + final String [] parts = constraint.split(" "); + boolean matches = false; + try { + if(constraint.startsWith(">") && parts.length == 2) { + final int value = Integer.valueOf(stringValue).intValue(); + matches = value > Integer.valueOf(parts[1]); + + } else if(constraint.startsWith("<") && parts.length == 2) { + final int value = Integer.valueOf(stringValue).intValue(); + matches = value < Integer.valueOf(parts[1]); + + } else if(parts.length == 4 && "between".equalsIgnoreCase(parts[0]) && "and".equalsIgnoreCase(parts[2]) ) { + final int value = Integer.valueOf(stringValue).intValue(); + final int lowerBound = Integer.valueOf(parts[1]); + final int upperBound = Integer.valueOf(parts[3]); + matches = value > lowerBound && value < upperBound; + + } else if(parts.length >1 && CONTAINS.equalsIgnoreCase(parts[0])) { + final String pattern = constraint.substring(CONTAINS.length()).trim(); + matches = stringValue.contains(pattern); + + } else { + matches = constraint.equals(stringValue); + } + } catch(NumberFormatException nfe) { + resultLog.add(new ResultLog.Entry( + Result.Status.WARN, + "Invalid numeric value [" + inputValue + "] while evaluating " + constraint)); + } + + if(matches) { + resultLog.add(new ResultLog.Entry( + Result.Status.DEBUG, + "Value [" + inputValue + "] matches constraint [" + constraint + "]")); + } else { + resultLog.add(new ResultLog.Entry( + Result.Status.WARN, + "Value [" + inputValue + "] does not match constraint [" + constraint + "]")); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/hc/util/package-info.java b/src/main/java/org/apache/sling/hc/util/package-info.java new file mode 100644 index 0000000..17a9969 --- /dev/null +++ b/src/main/java/org/apache/sling/hc/util/package-info.java @@ -0,0 +1,24 @@ +/* + * 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. + */ + +@Version("1.3.0") +package org.apache.sling.hc.util; + +import aQute.bnd.annotation.Version; + diff --git a/src/test/java/org/apache/sling/hc/api/ResultLogTest.java b/src/test/java/org/apache/sling/hc/api/ResultLogTest.java new file mode 100644 index 0000000..06a2349 --- /dev/null +++ b/src/test/java/org/apache/sling/hc/api/ResultLogTest.java @@ -0,0 +1,107 @@ +/* + * 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.sling.hc.api; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Iterator; + +import org.junit.Before; +import org.junit.Test; + +public class ResultLogTest { + + private ResultLog log; + + @Before + public void setup() { + log = new ResultLog(); + } + + @Test + public void testEmptyLogIsNotOk() { + assertEquals(Result.Status.WARN, log.getAggregateStatus()); + assertFalse(log.iterator().hasNext()); + } + + @Test + public void testSetStatusGoingUp() { + log.add(new ResultLog.Entry(Result.Status.DEBUG, "argh")); + assertEquals(Result.Status.OK, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.INFO, "argh")); + assertEquals(Result.Status.OK, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.OK, "argh")); + assertEquals(Result.Status.OK, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.WARN, "argh")); + assertEquals(Result.Status.WARN, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.CRITICAL, "argh")); + assertEquals(Result.Status.CRITICAL, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.HEALTH_CHECK_ERROR, "argh")); + assertEquals(Result.Status.HEALTH_CHECK_ERROR, log.getAggregateStatus()); + } + + @Test + public void testSetStatusGoingDownHCE() { + log.add(new ResultLog.Entry(Result.Status.HEALTH_CHECK_ERROR, "argh")); + assertEquals(Result.Status.HEALTH_CHECK_ERROR, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.CRITICAL, "argh")); + assertEquals(Result.Status.HEALTH_CHECK_ERROR, log.getAggregateStatus()); + } + + @Test + public void testSetStatusGoingDownCRIT() { + log.add(new ResultLog.Entry(Result.Status.CRITICAL, "argh")); + assertEquals(Result.Status.CRITICAL, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.WARN, "argh")); + assertEquals(Result.Status.CRITICAL, log.getAggregateStatus()); + } + + @Test + public void testSetStatusGoingDownWARN() { + log.add(new ResultLog.Entry(Result.Status.WARN, "argh")); + assertEquals(Result.Status.WARN, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.OK, "argh")); + assertEquals(Result.Status.WARN, log.getAggregateStatus()); + } + + @Test + public void testSetStatusGoingDownOK() { + log.add(new ResultLog.Entry(Result.Status.OK, "argh")); + assertEquals(Result.Status.OK, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.INFO, "argh")); + assertEquals(Result.Status.OK, log.getAggregateStatus()); + log.add(new ResultLog.Entry(Result.Status.DEBUG, "argh")); + assertEquals(Result.Status.OK, log.getAggregateStatus()); + } + + @Test + public void testLogEntries() { + log.add(new ResultLog.Entry(Result.Status.OK, "ok 1")); + log.add(new ResultLog.Entry(Result.Status.DEBUG, "debug 2")); + log.add(new ResultLog.Entry(Result.Status.WARN, "warn 3")); + log.add(new ResultLog.Entry(Result.Status.CRITICAL, "critical 4")); + + final Iterator<ResultLog.Entry> it = log.iterator(); + assertEquals("OK ok 1", it.next().toString()); + assertEquals("DEBUG debug 2", it.next().toString()); + assertEquals("WARN warn 3", it.next().toString()); + assertEquals("CRITICAL critical 4", it.next().toString()); + assertFalse(it.hasNext()); + } + } diff --git a/src/test/java/org/apache/sling/hc/api/ResultTest.java b/src/test/java/org/apache/sling/hc/api/ResultTest.java new file mode 100644 index 0000000..dff455d --- /dev/null +++ b/src/test/java/org/apache/sling/hc/api/ResultTest.java @@ -0,0 +1,65 @@ +/* + * 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.sling.hc.api; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Test; + +public class ResultTest { + + private final AtomicInteger counter = new AtomicInteger(); + + private void assertSingleResult(Result.Status toSet, Result.Status expected, boolean expectOk) { + final String msg = "test " + counter.incrementAndGet(); + final Result r = new Result(toSet, msg); + assertEquals(expected, r.getStatus()); + assertEquals(expectOk, r.isOk()); + assertTrue(r.iterator().hasNext()); + assertEquals(toSet.toString() + " " + msg, r.iterator().next().toString()); + } + + @Test + public void testSingleResult() { + assertSingleResult(Result.Status.DEBUG, Result.Status.OK, true); + assertSingleResult(Result.Status.INFO, Result.Status.OK, true); + assertSingleResult(Result.Status.OK, Result.Status.OK, true); + assertSingleResult(Result.Status.WARN, Result.Status.WARN, false); + assertSingleResult(Result.Status.CRITICAL, Result.Status.CRITICAL, false); + assertSingleResult(Result.Status.HEALTH_CHECK_ERROR, Result.Status.HEALTH_CHECK_ERROR, false); + } + + @Test + public void testLog() { + final ResultLog log = new ResultLog(); + log.add(new ResultLog.Entry(Result.Status.INFO, "argh")); + log.add(new ResultLog.Entry(Result.Status.WARN, "bad stuff")); + + final Result result = new Result(log); + assertEquals(Result.Status.WARN, result.getStatus()); + + final StringBuilder sb = new StringBuilder(); + for(ResultLog.Entry e : result) { + sb.append(e.toString()).append("#"); + } + assertEquals("INFO argh#WARN bad stuff#", sb.toString()); + } +} diff --git a/src/test/java/org/apache/sling/hc/util/SimpleConstraintCheckerTest.java b/src/test/java/org/apache/sling/hc/util/SimpleConstraintCheckerTest.java new file mode 100644 index 0000000..3a58eb0 --- /dev/null +++ b/src/test/java/org/apache/sling/hc/util/SimpleConstraintCheckerTest.java @@ -0,0 +1,167 @@ +/* + * 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.sling.hc.util; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.apache.sling.hc.api.Result; +import org.apache.sling.hc.api.ResultLog; +import org.apache.sling.hc.util.SimpleConstraintChecker; +import org.junit.Before; +import org.junit.Test; + +public class SimpleConstraintCheckerTest { + private ResultLog resultLog; + + private final SimpleConstraintChecker checker = new SimpleConstraintChecker(); + + @Before + public void setup() { + resultLog = new ResultLog(); + } + + @Test + public void testStringEquals() { + final String s = "test_" + System.currentTimeMillis(); + checker.check(s, s, resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testStringNotEquals() { + final String s = "test_" + System.currentTimeMillis(); + checker.check(s, "something else", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testFiveEquals() { + final String s = "5"; + checker.check(s, s, resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testIntTwelveEquals() { + checker.check(12, "12", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testIntTwelveGreaterThan() { + checker.check(12, "> 11", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testFiveNotEquals() { + checker.check("5", "foo", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testNullNotEquals() { + checker.check(null, "foo", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testNullNotGreater() { + checker.check(null, "> 2", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testGreaterThanTrue() { + checker.check("5", "> 2", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testGreaterThanFalse() { + checker.check("5", "> 12", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testLessThanTrue() { + checker.check("5", "< 12", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testLessThanFalse() { + checker.check("5", "< 2", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testBetweenA() { + checker.check("5", "between 2 and 6", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testBetweenB() { + checker.check("5", "between 12 and 16", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testBetweenC() { + checker.check(5L, "between 12 and 16", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testBetweenD() { + checker.check(5L, "between 4 and 16", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testBetweenE() { + checker.check(5L, "betWEEN 4 aND 16", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testNotAnInteger() { + checker.check("foo", "between 12 and 16", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testContainsA() { + checker.check("This is a NICE STRING ok?", "contains NICE STRING", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testContainsB() { + checker.check("This is a NICE TOUCH ok?", "contains NICE STRING", resultLog); + assertFalse(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } + + @Test + public void testContainsC() { + checker.check("This is a NICE TOUCH ok?", "contains NICE", resultLog); + assertTrue(resultLog.getAggregateStatus().equals(Result.Status.OK)); + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
