| /* |
| * 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.support.impl; |
| |
| import java.util.Arrays; |
| import java.util.Map; |
| |
| import javax.servlet.http.HttpServletRequest; |
| |
| import org.apache.felix.scr.annotations.Activate; |
| import org.apache.felix.scr.annotations.Component; |
| import org.apache.felix.scr.annotations.ConfigurationPolicy; |
| import org.apache.felix.scr.annotations.Properties; |
| import org.apache.felix.scr.annotations.Property; |
| import org.apache.felix.scr.annotations.PropertyUnbounded; |
| import org.apache.felix.scr.annotations.Reference; |
| import org.apache.felix.scr.annotations.Service; |
| import org.apache.sling.api.resource.ResourceResolver; |
| import org.apache.sling.api.resource.ResourceResolverFactory; |
| import org.apache.sling.commons.osgi.PropertiesUtil; |
| import org.apache.sling.engine.SlingRequestProcessor; |
| import org.apache.sling.hc.api.HealthCheck; |
| import org.apache.sling.hc.api.Result; |
| import org.apache.sling.hc.util.FormattingResultLog; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** {@link HealthCheck} that checks the HTTP status of Sling requests. |
| * Typically used to check that a freshly installed Sling-based system |
| * is in good shape, contains all required content etc. */ |
| @Component( |
| name="org.apache.sling.hc.support.SlingRequestStatusHealthCheck", |
| configurationFactory=true, |
| policy=ConfigurationPolicy.REQUIRE, |
| metatype=true, |
| label="Apache Sling Request Status Health Check", |
| description="Checks the HTTP status of Sling requests.") |
| @Properties({ |
| @Property(name=HealthCheck.NAME, |
| label="Health Check Name", description="Name of this Health Check service."), |
| @Property(name=HealthCheck.TAGS, unbounded=PropertyUnbounded.ARRAY, |
| label="Health Check tags", description="List of tags for this Health Check service, used to select " + |
| "subsets of Health Check services for execution"), |
| @Property(name=HealthCheck.MBEAN_NAME, |
| label="MBean Name", description="Name of the MBean to create for this Health Check.") |
| }) |
| @Service(value=HealthCheck.class) |
| public class SlingRequestStatusHealthCheck implements HealthCheck { |
| |
| private static final Logger log = LoggerFactory.getLogger(SlingRequestStatusHealthCheck.class); |
| private String [] paths; |
| |
| static class PathSpec { |
| int status; |
| String path; |
| |
| PathSpec(String configuredPath, FormattingResultLog resultLog) { |
| path = configuredPath; |
| status = 200; |
| |
| final String [] parts = configuredPath.split(":"); |
| if(parts.length == 2) { |
| try { |
| status = Integer.valueOf(parts[1].trim()); |
| path = parts[0].trim(); |
| } catch(NumberFormatException nfe) { |
| resultLog.healthCheckError("NumberFormatException while parsing [{}], invalid status value?", configuredPath); |
| } |
| } |
| } |
| } |
| |
| @Property(unbounded=PropertyUnbounded.ARRAY, |
| label="Paths to Check", |
| description="The list of paths to check, optionally with expected HTTP status responses. " + |
| "An entry like \"/tmp/test.txt:301\", for example, checks that /tmp/test.txt returns a " + |
| "301 response.") |
| private static final String PROP_PATH = "path"; |
| |
| @Reference |
| private SlingRequestProcessor requestProcessor; |
| |
| @Reference |
| private ResourceResolverFactory resolverFactory; |
| |
| @Activate |
| public void activate(final Map<String, Object> properties) { |
| paths = PropertiesUtil.toStringArray(properties.get(PROP_PATH), new String [] {}); |
| log.info("Activated, paths={}", Arrays.asList(paths)); |
| } |
| |
| @Override |
| public Result execute() { |
| final FormattingResultLog resultLog = new FormattingResultLog(); |
| |
| ResourceResolver resolver = null; |
| int checked = 0; |
| int failed = 0; |
| String lastPath = null; |
| |
| try { |
| resolver = resolverFactory.getAdministrativeResourceResolver(null); |
| for(String p : paths) { |
| lastPath = p; |
| final PathSpec ps = new PathSpec(p, resultLog); |
| final HttpServletRequest request = new InternalRequest(ps.path); |
| final InternalResponse response = new InternalResponse(); |
| requestProcessor.processRequest(request, response, resolver); |
| final int status = response.getStatus(); |
| if(status != ps.status) { |
| failed++; |
| resultLog.warn("[{}] returns status {}, expected {}", new Object[] { ps.path, status, ps.status }); |
| } else { |
| resultLog.debug("[{}] returns status {} as expected", ps.path, status); |
| } |
| checked++; |
| } |
| } catch(Exception e) { |
| resultLog.warn("Exception while executing request [{}]: {}", lastPath, e); |
| } finally { |
| if(resolver != null) { |
| resolver.close(); |
| } |
| } |
| |
| if(checked == 0) { |
| resultLog.warn("No paths checked, empty paths list?"); |
| } else { |
| resultLog.info("{} paths checked, {} failures", checked, failed); |
| } |
| |
| return new Result(resultLog); |
| } |
| } |