Author: cziegeler
Date: Mon Aug 19 10:46:00 2013
New Revision: 1515349
URL: http://svn.apache.org/r1515349
Log:
SLING-3021 : Use service properties for HC meta data and improve JMX
registration
Modified:
sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBean.java
sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanCreator.java
sling/trunk/contrib/extensions/healthcheck/jmx/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java
Modified:
sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBean.java
URL:
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBean.java?rev=1515349&r1=1515348&r2=1515349&view=diff
==============================================================================
---
sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBean.java
(original)
+++
sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBean.java
Mon Aug 19 10:46:00 2013
@@ -17,8 +17,8 @@
*/
package org.apache.sling.hc.jmx.impl;
-import java.io.Serializable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -46,21 +46,13 @@ import org.apache.sling.hc.api.HealthChe
import org.apache.sling.hc.api.Result;
import org.apache.sling.hc.api.ResultLog;
import org.osgi.framework.ServiceReference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
/** A {@link DynamicMBean} used to execute a {@link HealthCheck} service */
-public class HealthCheckMBean implements DynamicMBean, Serializable {
-
- private static final long serialVersionUID = -90745301105975287L;
- private static final Logger logger =
LoggerFactory.getLogger(HealthCheckMBean.class);
- private final String beanName;
- private final String jmxTypeName;
- private final HealthCheck healthCheck;
+public class HealthCheckMBean implements DynamicMBean {
public static final String HC_OK_ATTRIBUTE_NAME = "ok";
public static final String HC_STATUS_ATTRIBUTE_NAME = "status";
- public static final String LOG_ATTRIBUTE_NAME = "log";
+ public static final String HC_LOG_ATTRIBUTE_NAME = "log";
private static CompositeType LOG_ROW_TYPE;
private static TabularType LOG_TABLE_TYPE;
@@ -69,149 +61,174 @@ public class HealthCheckMBean implements
public static final String LEVEL_COLUMN = "level";
public static final String MESSAGE_COLUMN = "message";
- public static final String DEFAULT_JMX_TYPE_NAME = "HealthCheck";
+ public static final String JMX_TYPE_NAME = "HealthCheck";
+ public static final String JMX_DOMAIN = "org.apache.sling.healthcheck";
+
+ /** The health check service to call. */
+ private final HealthCheck healthCheck;
+
+ /** The mbean info. */
+ private final MBeanInfo mbeanInfo;
- private final ServiceReference serviceReference;
+ /** 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 Rule log",
+ "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", "Rule log messages",
LOG_ROW_TYPE, indexes);
+ 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 ref, final HealthCheck hc) {
- this.serviceReference = ref;
- String name = (String)ref.getProperty(HealthCheck.MBEAN_NAME);
- if(empty(name)) {
- name = (String)ref.getProperty(HealthCheck.NAME);
- }
-
- if(empty(name)) {
- name = hc.toString();
- }
-
- final int pos = name.indexOf('/');
- if(pos > 0) {
- jmxTypeName = name.substring(0, pos);
- beanName = name.substring(pos + 1);
- } else {
- jmxTypeName = DEFAULT_JMX_TYPE_NAME;
- beanName = name;
- }
-
- healthCheck = hc;
- }
-
- private static boolean empty(String str) {
- return str == null || str.trim().length() == 0;
+ this.healthCheck = hc;
+ this.mbeanInfo = this.createMBeanInfo(ref);
+ this.defaultAttributes = this.createDefaultAttributes(ref);
}
@Override
public Object getAttribute(final String attribute)
- throws AttributeNotFoundException, MBeanException,
ReflectionException {
-
- // TODO cache the result of execution for a few seconds?
- final Result result = healthCheck.execute();
-
- if(HC_OK_ATTRIBUTE_NAME.equals(attribute)) {
- return result.isOk();
- } else if(LOG_ATTRIBUTE_NAME.equals(attribute)) {
- return logData(result);
- } else if(HC_STATUS_ATTRIBUTE_NAME.equals(attribute)) {
- return result.getStatus().toString();
- } else {
- final Object o = this.serviceReference.getProperty(attribute);
- if(o == null) {
- throw new AttributeNotFoundException(attribute);
- }
- return o;
+ 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(Result er) {
+ private TabularData logData(final Result er) throws OpenDataException {
final TabularDataSupport result = new
TabularDataSupport(LOG_TABLE_TYPE);
- int i=1;
- for(ResultLog.Entry e : er) {
+ 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.getStatus().toString());
data.put(MESSAGE_COLUMN, e.getMessage());
- try {
- result.put(new CompositeDataSupport(LOG_ROW_TYPE, data));
- } catch(OpenDataException ode) {
- throw new IllegalStateException("OpenDataException while
creating log data", ode);
- }
+
+ result.put(new CompositeDataSupport(LOG_ROW_TYPE, data));
}
return result;
}
@Override
- public AttributeList getAttributes(String[] attributes) {
+ public AttributeList getAttributes(final String[] attributes) {
final AttributeList result = new AttributeList();
- for(String key : attributes) {
- try {
- result.add(new Attribute(key, getAttribute(key)));
- } catch(Exception e) {
- logger.error("Exception getting Attribute " + key, e);
+ if ( attributes != null ) {
+ Result hcResult = null;
+ for(final String key : attributes) {
+ final Object defaultValue = this.defaultAttributes.get(key);
+ if ( defaultValue != null ) {
+ result.add(new Attribute(key, defaultValue));
+ } else {
+ if ( HC_OK_ATTRIBUTE_NAME.equals(key) ) {
+ if ( hcResult == null ) {
+ hcResult = this.healthCheck.execute();
+ }
+ result.add(new Attribute(key, hcResult.isOk()));
+ } else if ( HC_LOG_ATTRIBUTE_NAME.equals(key) ) {
+ if ( hcResult == null ) {
+ hcResult = this.healthCheck.execute();
+ }
+ try {
+ result.add(new Attribute(key, logData(hcResult)));
+ } catch ( final OpenDataException ignore ) {
+ // we ignore this and simply don't add the
attribute
+ }
+ } else if ( HC_STATUS_ATTRIBUTE_NAME.equals(key) ) {
+ if ( hcResult == null ) {
+ hcResult = this.healthCheck.execute();
+ }
+ result.add(new Attribute(key,
hcResult.getStatus().toString()));
+ }
+ }
}
}
+
return result;
}
- @Override
- public MBeanInfo getMBeanInfo() {
+ /**
+ * Create the mbean info
+ */
+ private MBeanInfo createMBeanInfo(final ServiceReference serviceReference)
{
final List<MBeanAttributeInfo> attrs = new
ArrayList<MBeanAttributeInfo>();
// add relevant service properties
- final String[] serviceProps = new String[] {HealthCheck.NAME,
HealthCheck.TAGS};
- for(final String propName : serviceProps) {
- if ( this.serviceReference.getProperty(propName) != null ) {
- attrs.add(new MBeanAttributeInfo(propName,
List.class.getName(), "Description of " + propName, true, false, false));
- }
+ 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 HealthCheck result", true, false, false));
- attrs.add(new OpenMBeanAttributeInfoSupport(LOG_ATTRIBUTE_NAME, "The
rule log", LOG_TABLE_TYPE, true, false, false));
- attrs.add(new MBeanAttributeInfo(HC_STATUS_ATTRIBUTE_NAME,
String.class.getName(), "The HealthCheck status", true, false, false));
+ 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 OpenMBeanAttributeInfoSupport(HC_LOG_ATTRIBUTE_NAME,
"The health check result log", LOG_TABLE_TYPE, true, false, false));
+
+ return new MBeanInfo(this.getClass().getName(),
+ "Health check",
+ attrs.toArray(new MBeanAttributeInfo[attrs.size()]), null,
null, null);
+ }
+
+ /**
+ * Create the default attributes.
+ */
+ private Map<String, Object> createDefaultAttributes(final ServiceReference
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;
+ }
- return new MBeanInfo(this.getClass().getName(), beanName,
attrs.toArray(new MBeanAttributeInfo[attrs.size()]), null, null, null);
+ @Override
+ public MBeanInfo getMBeanInfo() {
+ return this.mbeanInfo;
}
@Override
- public Object invoke(String actionName, Object[] params, String[]
signature)
+ public Object invoke(final String actionName, final Object[] params, final
String[] signature)
throws MBeanException, ReflectionException {
- throw new UnsupportedOperationException(getClass().getSimpleName() + "
does not support operations on Rules");
+ throw new MBeanException(new
UnsupportedOperationException(getClass().getSimpleName() + " does not support
operations."));
}
@Override
- public void setAttribute(Attribute attribute)
- throws AttributeNotFoundException, InvalidAttributeValueException,
+ public void setAttribute(final Attribute attribute)
+ throws AttributeNotFoundException, InvalidAttributeValueException,
MBeanException, ReflectionException {
- throw new UnsupportedOperationException(getClass().getSimpleName() + "
does not support setting Rules attributes");
+ throw new MBeanException(new
UnsupportedOperationException(getClass().getSimpleName() + " does not support
setting attributes."));
}
@Override
- public AttributeList setAttributes(AttributeList attributes) {
- throw new UnsupportedOperationException(getClass().getSimpleName() + "
does not support setting Rules attributes");
- }
-
- public String getName() {
- return beanName;
+ public AttributeList setAttributes(final AttributeList attributes) {
+ return new AttributeList();
}
- public String getJmxTypeName() {
- return jmxTypeName;
+ @Override
+ public String toString() {
+ return "HealthCheckMBean [healthCheck=" + healthCheck + "]";
}
}
\ No newline at end of file
Modified:
sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanCreator.java
URL:
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanCreator.java?rev=1515349&r1=1515348&r2=1515349&view=diff
==============================================================================
---
sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanCreator.java
(original)
+++
sling/trunk/contrib/extensions/healthcheck/jmx/src/main/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanCreator.java
Mon Aug 19 10:46:00 2013
@@ -17,9 +17,12 @@
*/
package org.apache.sling.hc.jmx.impl;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
+import java.util.List;
import java.util.Map;
import javax.management.DynamicMBean;
@@ -32,51 +35,47 @@ import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates an {@link HealthCheckMbean} for every {@link HealthCheckMBean}
service
*
- * TODO: What happens if two mbeans want to use the same object name (type and
name).
- * We need to handle this and maybe use service ranking to resolve the
conflict
- * */
+ */
@Component
public class HealthCheckMBeanCreator {
- private final Logger log = LoggerFactory.getLogger(getClass());
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final Map<ServiceReference, Registration> registeredServices = new
HashMap<ServiceReference, Registration>();
- private final Map<ServiceReference, ServiceRegistration>
registeredServices = new HashMap<ServiceReference, ServiceRegistration>();
+ private final Map<String, List<ServiceReference>> sortedRegistrations =
new HashMap<String, List<ServiceReference>>();
private ServiceTracker hcTracker;
@Activate
protected void activate(final BundleContext btx) {
- this.hcTracker = new ServiceTracker(btx, HealthCheck.class.getName(),
new ServiceTrackerCustomizer() {
+ this.hcTracker = new ServiceTracker(btx, HealthCheck.class.getName(),
null) {
@Override
- public synchronized void removedService(final ServiceReference
reference, final Object service) {
- btx.ungetService(reference);
- unregisterHCMBean(reference);
+ public Object addingService(final ServiceReference reference) {
+ return registerHCMBean(btx, reference);
}
@Override
- public synchronized void modifiedService(final ServiceReference
reference, final Object service) {
- unregisterHCMBean(reference);
- registerHCMBean(btx, reference, (HealthCheck)service);
+ public void modifiedService(final ServiceReference reference,
+ final Object service) {
+ unregisterHCMBean(btx, reference);
+ registerHCMBean(btx, reference);
}
@Override
- public synchronized Object addingService(final ServiceReference
reference) {
- final HealthCheck hc = (HealthCheck) btx.getService(reference);
-
- if ( hc != null ) {
- registerHCMBean(btx, reference, hc);
- }
- return hc;
+ public void removedService(final ServiceReference reference,
+ final Object service) {
+ unregisterHCMBean(btx, reference);
}
- });
+
+ };
this.hcTracker.open();
}
@@ -88,22 +87,103 @@ public class HealthCheckMBeanCreator {
}
}
- private void registerHCMBean(final BundleContext bundleContext, final
ServiceReference ref, final HealthCheck hc) {
- final HealthCheckMBean mbean = new HealthCheckMBean(ref, hc);
-
- final Dictionary<String, String> mbeanProps = new Hashtable<String,
String>();
- mbeanProps.put("jmx.objectname", "org.apache.sling.healthcheck:type="
+ mbean.getJmxTypeName() + ",service=" + mbean.getName());
+ /**
+ * Register an mbean for a health check service.
+ * The mbean is only registered if
+ * - the service has an mbean registration property
+ * - if there is no other service with the same name but a higher service
ranking
+ *
+ * @param bundleContext The bundle context
+ * @param reference The service reference to the health check service
+ * @return The registered mbean or <code>null</code>
+ */
+ private synchronized Object registerHCMBean(final BundleContext
bundleContext, final ServiceReference reference) {
+ final Registration reg = Registration.getRegistration(bundleContext,
reference);
+ if ( reg != null ) {
+ this.registeredServices.put(reference, reg);
- final ServiceRegistration reg =
bundleContext.registerService(DynamicMBean.class.getName(), mbean, mbeanProps);
- registeredServices.put(ref, reg);
- log.debug("Registered {} with properties {}", mbean, mbeanProps);
+ List<ServiceReference> registered =
this.sortedRegistrations.get(reg.name);
+ if ( registered == null ) {
+ registered = new ArrayList<ServiceReference>();
+ this.sortedRegistrations.put(reg.name, registered);
+ }
+ registered.add(reference);
+ Collections.sort(registered);
+ if ( registered.get(0).equals(reference) ) {
+ if ( registered.size() > 1 ) {
+ final ServiceReference prevRef = registered.get(1);
+ final Registration prevReg =
this.registeredServices.get(prevRef);
+ prevReg.unregister(this.logger);
+ }
+ reg.register(this.logger, bundleContext);
+ }
+ }
+ return reg;
}
- private void unregisterHCMBean(final ServiceReference ref) {
- final ServiceRegistration reg = registeredServices.remove(ref);
+ private synchronized void unregisterHCMBean(final BundleContext
bundleContext, final ServiceReference ref) {
+ final Registration reg = registeredServices.remove(ref);
if ( reg != null ) {
- reg.unregister();
- log.debug("Ungegistered {}", ref);
+ final boolean registerFirst = reg.unregister(this.logger);
+ final List<ServiceReference> registered =
this.sortedRegistrations.get(reg.name);
+ registered.remove(ref);
+ if ( registered.size() == 0 ) {
+ this.sortedRegistrations.remove(reg.name);
+ } else if ( registerFirst ) {
+ final ServiceReference newRef = registered.get(0);
+ final Registration newReg =
this.registeredServices.get(newRef);
+ newReg.register(this.logger, bundleContext);
+ }
+ bundleContext.ungetService(ref);
+ }
+ }
+
+ private static final class Registration {
+ public final String name;
+ public final HealthCheckMBean mbean;
+ private ServiceRegistration registration;
+
+ public Registration(final String name, final HealthCheckMBean mbean) {
+ this.name = name;
+ this.mbean = mbean;
+ }
+
+ public static Registration getRegistration(final BundleContext
bundleContext, final ServiceReference ref) {
+ final Object nameObj = ref.getProperty(HealthCheck.MBEAN_NAME);
+ if ( nameObj != null ) {
+ final HealthCheck service = (HealthCheck)
bundleContext.getService(ref);
+ if ( service != null ) {
+ final HealthCheckMBean mbean = new HealthCheckMBean(ref,
service);
+
+ return new Registration(nameObj.toString().replace(',',
'.'), mbean);
+ }
+ }
+ return null;
+ }
+
+ public void register(final Logger logger, final BundleContext btx) {
+ final StringBuilder sb = new
StringBuilder(HealthCheckMBean.JMX_DOMAIN);
+ sb.append(":type=");
+ sb.append(HealthCheckMBean.JMX_TYPE_NAME);
+ sb.append(",name=");
+ sb.append(this.name);
+ final String objectName = sb.toString();
+
+ final Dictionary<String, String> mbeanProps = new
Hashtable<String, String>();
+ mbeanProps.put("jmx.objectname", objectName);
+ this.registration =
btx.registerService(DynamicMBean.class.getName(), this.mbean, mbeanProps);
+
+ logger.debug("Registered health check mbean {} as {}", this.mbean,
objectName);
+ }
+
+ public boolean unregister(final Logger logger) {
+ if ( this.registration != null ) {
+ this.registration.unregister();
+ this.registration = null;
+ logger.debug("Ungegistered health check mbean {}", this.mbean);
+ return true;
+ }
+ return false;
}
}
}
\ No newline at end of file
Modified:
sling/trunk/contrib/extensions/healthcheck/jmx/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java
URL:
http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/healthcheck/jmx/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java?rev=1515349&r1=1515348&r2=1515349&view=diff
==============================================================================
---
sling/trunk/contrib/extensions/healthcheck/jmx/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java
(original)
+++
sling/trunk/contrib/extensions/healthcheck/jmx/src/test/java/org/apache/sling/hc/jmx/impl/HealthCheckMBeanTest.java
Mon Aug 19 10:46:00 2013
@@ -29,7 +29,6 @@ import org.apache.sling.hc.api.HealthChe
import org.apache.sling.hc.api.Result;
import org.apache.sling.hc.api.ResultLog;
import org.apache.sling.hc.healthchecks.util.SimpleConstraintChecker;
-import org.apache.sling.hc.jmx.impl.HealthCheckMBean;
import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceReference;
@@ -60,7 +59,7 @@ public class HealthCheckMBeanTest {
final Object value = jmxServer.getAttribute(objectName, attributeName);
final ResultLog resultLog = new ResultLog();
new SimpleConstraintChecker().check(value, constraint, resultLog);
- assertEquals("Expecting result " + expected, expected,
resultLog.getAggregateStatus().equals(Result.Status.OK));
+ assertEquals("Expecting result " + expected + "(" + resultLog + ")",
expected, resultLog.getAggregateStatus().equals(Result.Status.OK));
}