/* * Copyright 2012 Jörg Hoh, Alexander Saar, Markus Haack * * 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. */ package de.joerghoh.cq5.healthcheck.impl; import java.io.IOException; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; import org.apache.sling.commons.osgi.PropertiesUtil; import org.apache.sling.jcr.api.SlingRepository; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.day.cq.jcrclustersupport.ClusterAware; import de.joerghoh.cq5.healthcheck.StatusService; import de.joerghoh.cq5.healthcheck.StatusCode; /** * A servlet, which returns the actual status suitable for a loadbalancer. * * Internally the HealthStatusService and the current clustering status is used * to determine the return code. * * This code allows to specify a cluster strategy: * * ActiveActive: All cluster nodes are eligable to return "OK" * * ActivePassive: Only the master is eligable to return "OK", slaves always return "WARN". * * In any case also the OverallStatus of the HealthStatusService is used to * identify the status. If the status is != OK, then "WARN" is returned and the * return code of this request is set to 500 (internal server error). * * @author joerg */ @Component(immediate = true, metatype = true, label="Loadbalancer director service", description="Service managing the loadbalancer based on the healtchechk data ") @Service(value = {javax.servlet.Servlet.class, com.day.cq.jcrclustersupport.ClusterAware.class}) @Properties({ @Property(name = "sling.servlet.methods", value = "GET", propertyPrivate=true), @Property(name = "sling.servlet.resourceTypes", value = "healthcheck/servlet/loadbalancer", propertyPrivate=true) }) public class LoadbalancerStatus extends SlingSafeMethodsServlet implements ClusterAware { @Reference private StatusService statusService; @Reference private SlingRepository repository; private static final String DEFAULT_LB_STRATEGY = "ActivePassive"; @Property(value = DEFAULT_LB_STRATEGY, description = "Specify your clustering strategy to instruct the loadbalancer. " + "Currently 'ActivePassive' and 'ActiveActive' are supported.") private static final String PROPERTY_LB_STRATEGY = "strategy"; private boolean iAmMaster = false; private String loadbalancerStrategy; private Logger log = LoggerFactory.getLogger(LoadbalancerStatus.class); private static final long serialVersionUID = -8012558085365805331L; @Override public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) { try { StatusCode status = statusService.getStatus().getStatus(); boolean allServicesOK = ( status == StatusCode.OK ); boolean statusOK = false; if (loadbalancerStrategy.equals("ActivePassive")) { statusOK = iAmMaster && allServicesOK; } else if (loadbalancerStrategy.equals("ActiveActive")) { statusOK = allServicesOK; } else { statusOK = false; log.error("Invalid loadbalancer strategy set: " + loadbalancerStrategy); } response.setContentType("text/html"); if (statusOK) { response.setStatus(200); response.getOutputStream().print(StatusCode.OK.toString()); } else { // in case of problems return an errorcode response.setStatus(500); response.getOutputStream().print(StatusCode.WARN.toString()); } response.getOutputStream().flush(); } catch (IOException e) { log.warn("Cannot write to output: " + e.getMessage()); } } /** ClusterAware impl * * be cautious: this approach detects only changes to the cluster state, * but the initial status must be detected via another mechanism. * * **/ public void bindRepository(String repositoryId, String clusterId, boolean isMaster) { iAmMaster = isMaster; } public void unbindRepository() { log.warn("Repository is unbound, this should not happen!"); } /** SCR **/ @Activate public void Activate(ComponentContext ctx) { loadbalancerStrategy = PropertiesUtil.toString( ctx.getProperties().get(PROPERTY_LB_STRATEGY), DEFAULT_LB_STRATEGY); log.info("Using loadbalancer strategy " + loadbalancerStrategy); String v = repository.getDescriptor("crx.cluster.master"); iAmMaster = (v == null || Boolean.parseBoolean(v)); } }