/* | |
* 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.annotations; | |
import java.util.List; | |
import org.apache.felix.scrplugin.SCRDescriptorException; | |
import org.apache.felix.scrplugin.SCRDescriptorFailureException; | |
import org.apache.felix.scrplugin.annotations.AnnotationProcessor; | |
import org.apache.felix.scrplugin.annotations.ClassAnnotation; | |
import org.apache.felix.scrplugin.annotations.ScannedClass; | |
import org.apache.felix.scrplugin.description.ClassDescription; | |
import org.apache.felix.scrplugin.description.ComponentConfigurationPolicy; | |
import org.apache.felix.scrplugin.description.ComponentDescription; | |
import org.apache.felix.scrplugin.description.PropertyDescription; | |
import org.apache.felix.scrplugin.description.PropertyType; | |
import org.apache.felix.scrplugin.description.PropertyUnbounded; | |
import org.apache.felix.scrplugin.description.ServiceDescription; | |
import org.apache.sling.hc.api.HealthCheck; | |
/** Annotation processor for the SlingHealthCheck annotation. */ | |
public class SlingHealthCheckProcessor implements AnnotationProcessor { | |
@Override | |
public void process(final ScannedClass scannedClass, final ClassDescription classDescription) throws SCRDescriptorException, SCRDescriptorFailureException { | |
final List<ClassAnnotation> healthChecks = scannedClass.getClassAnnotations(SlingHealthCheck.class.getName()); | |
scannedClass.processed(healthChecks); | |
for (final ClassAnnotation cad : healthChecks) { | |
processHealthCheck(cad, classDescription); | |
} | |
} | |
/** Processes the given healthcheck annotation. | |
* | |
* @param cad the annotation | |
* @param classDescription the class description */ | |
private void processHealthCheck(final ClassAnnotation cad, final ClassDescription classDescription) { | |
final boolean generateComponent = cad.getBooleanValue("generateComponent", true); | |
final boolean metatype = cad.getBooleanValue("metatype", true); | |
final boolean immediate = cad.getBooleanValue("immediate", false); | |
// generate ComponentDescription if required | |
if (generateComponent) { | |
String nameOfAnnotatedClass = classDescription.getDescribedClass().getName(); | |
final ComponentDescription cd = new ComponentDescription(cad); | |
cd.setName(cad.getStringValue("componentName", nameOfAnnotatedClass)); | |
cd.setConfigurationPolicy(ComponentConfigurationPolicy.valueOf(cad.getEnumValue("configurationPolicy", | |
ComponentConfigurationPolicy.OPTIONAL.name()))); | |
cd.setSetMetatypeFactoryPid(cad.getBooleanValue("configurationFactory", false)); | |
String nameFromAnnotation = (String) cad.getValue("name"); | |
String defaultLabel = "Sling Health Check: " + (nameFromAnnotation!=null ? nameFromAnnotation : nameOfAnnotatedClass); | |
cd.setLabel(cad.getStringValue("label", defaultLabel)); | |
cd.setDescription(cad.getStringValue("description", "Health Check Configuration")); | |
cd.setCreateMetatype(metatype); | |
cd.setImmediate(immediate); | |
classDescription.add(cd); | |
} | |
// generate ServiceDescription if required | |
final boolean generateService = cad.getBooleanValue("generateService", true); | |
if (generateService) { | |
final ServiceDescription sd = new ServiceDescription(cad); | |
sd.addInterface(HealthCheck.class.getName()); | |
classDescription.add(sd); | |
} | |
// generate HC PropertyDescriptions | |
generatePropertyDescriptor(cad, classDescription, metatype, "name", HealthCheck.NAME, PropertyType.String, | |
"Name", "Name of the Health Check", false); | |
generatePropertyDescriptor(cad, classDescription, metatype, "tags", HealthCheck.TAGS, PropertyType.String, | |
"Tags", "List of tags", true); | |
generatePropertyDescriptor(cad, classDescription, metatype, "mbeanName", HealthCheck.MBEAN_NAME, PropertyType.String, | |
"MBean", "MBean name (leave empty for not using JMX)", false); | |
generatePropertyDescriptor(cad, classDescription, metatype, "asyncCronExpression", HealthCheck.ASYNC_CRON_EXPRESSION, PropertyType.String, | |
"Cron expression", "Cron expression for asynchronous execution (leave empty for synchronous execution)", false); | |
String resultCacheTtlInMsPropName = "resultCacheTtlInMs"; | |
if (cad.getValue(resultCacheTtlInMsPropName) != null) { | |
generatePropertyDescriptor(cad, classDescription, metatype, resultCacheTtlInMsPropName, HealthCheck.RESULT_CACHE_TTL_IN_MS, PropertyType.Long, | |
"Result Cache TTL", | |
"TTL for results. The value -1 (default) uses the global configuration in health check executor. " | |
+ "Redeployment of a HC always invalidates its cached result.", | |
false); | |
} | |
String warningsStickForMinutesPropName = "warningsStickForMinutes"; | |
if (cad.getValue(warningsStickForMinutesPropName) != null) { | |
generatePropertyDescriptor(cad, classDescription, metatype, warningsStickForMinutesPropName, | |
"hc.warningsStickForMinutes" /* use constant once API is released */, | |
PropertyType.Long, "Sticky Warnings", | |
"If given, warning results (that is WARN, CRITICAL or HEALTH_CHECK_ERROR) from the past executions will be " | |
+ "taken into account as well for the given minutes (use Integer.MAX_VALUE for indefinitely). " | |
+ "By default this is disabled (value -1).", | |
false); | |
} | |
} | |
private boolean isLongDefaultValue(final ClassAnnotation cad, String propName) { | |
Object value = cad.getValue(propName); | |
return value == null || ((Long) value) == -1; | |
} | |
/** Generates a property descriptor of type {@link PropertyType} */ | |
private void generatePropertyDescriptor(final ClassAnnotation cad, final ClassDescription classDescription, | |
final boolean metatype, final String propertyName, final String propertyDescriptorName, PropertyType propertyType, String label, String description, boolean isArray) { | |
final PropertyDescription pd = new PropertyDescription(cad); | |
pd.setName(propertyDescriptorName); | |
pd.setLabel(label); | |
pd.setDescription(description); | |
pd.setType(propertyType); | |
if(isArray) { | |
final String[] values = (String[]) cad.getValue(propertyName); | |
pd.setMultiValue(values); | |
pd.setUnbounded(PropertyUnbounded.ARRAY); | |
pd.setCardinality(Integer.MAX_VALUE); | |
} else { | |
final Object propertyVal = cad.getValue(propertyName); | |
String pdValue = (propertyVal instanceof String) ? (String) propertyVal : | |
propertyVal!=null ? propertyVal.toString() : null; | |
pd.setValue(pdValue); | |
pd.setUnbounded(PropertyUnbounded.DEFAULT); | |
} | |
if (!metatype) { | |
pd.setPrivate(true); | |
} | |
classDescription.add(pd); | |
} | |
@Override | |
public int getRanking() { | |
return 500; | |
} | |
@Override | |
public String getName() { | |
return SlingHealthCheck.class.getName() + " annotation processor."; | |
} | |
} |