| /* |
| * 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); |
| } |
| } |