Dashboard development

From jManage

Overview

jManage allows users to aggregate various information of an application in the form of customized dashboards for easy of use. jManage provides couple of out-of-the-box dashboards that present JVM related information for Java 5 based applications. Also, provides a framework that allows users to easily plug-in their custom built dashboards.

Framework components

jManage has well defined elements that form a dashboard. Each of these elements are explained in this section and the next section demonstrates how to use these elements to build a custom dashboard.

Dashboard configuration file: This is an XML file with intuitive tags/elements. This is a mandatory element of a dashboard which is used to configure various elements of a dashboard. Core framework transforms this configuration into a dashboard web page. Template for the web page is also configured in the XML file.

Dashboard component: Component is a single unit of information that is displayed on a dashboard and is represented by org.jmanage.webui.dashboard.framework.DashboardComponent.java. This is an optional element which should be implemented if none of the out-of-the-box components doesn't serve the purpose. Some of the out-of-the-box components are,

    Graph component - To chart the MBean attribute value(s).
    MBean attribute value - To present the value of a given MBean attribute in the form shown below.
         Attribute Display Name : Attribute Value
    Select List - To show MBean attributes in a select box with an onSelect() event support. Today this is not a re-usable
    component but might refactor it in future.
    MBean operation result - To execute and MBean operation.

Dashboard qualifier: Qualifier is responsible for verifying the eligibility of a dashboard to get associated with a configured application. Whenever an application is configured these verifications are executed. This is represented by org.jmanage.webui.dashboard.framework.BaseDashboardQualifier.java. like components this is also an optional element. jManage provides two out-of-the-box qualifiers which are, Generic MBean check qualifier - To check existence of a given MBean in the application being configured. Dashboard gets associated with the application if the specified MBean was found. Java5 dashboard qualifier - To check if the application configured is a Java 5 based application and the dashboard gets associated based on the outcome.

Dashboard template page: This is a JSP template having place holders for each of the dashboard components. It uses custom tags to render the components of a dashboard.

Dashboard component tag: A JSP custom tag that reads dashboard component configuration and renders the same on template JSP.

Building custom dashboards

Create dashboard configuration XML file

Configuration file is a mandatory element of dashboard. It describes how various dashboard elements should be used in order to present a dashboard. A sample configuration file and explanation of various elements/attributes are below.

<dashboard id="jvmSummary" name="JVM Summary" template="/dashboard/jvmSummary.jsp">
 <qualifications comment="defines what type of applications qualify for this dashboard">
  <qualifier class="org.jmanage.webui.dashboard.qualifiers.Java5DashboardQualifier">
     <property name="mbean" value="java.lang:type=Runtime"/>
     <property name="attribute" value="VmVersion"/>
  </qualifier>
 </qualifications>
 <components>
  <component id="com1" class="org.jmanage.webui.dashboard.components.MBeanAttributeValue" name="Single attribute value" refreshInterval="5000">
     <property name="mbean" value="java.lang:type=Runtime"/>
     <property name="attribute" value="Uptime"/>
     <property name="displayName" value="App up time (milliseconds)"/>
  </component>
 </components>
</dashboard>

<dashboard>: Root element of dashboard configuration file with following attributes,

    "id" - unique id across jManage dashboards, it can be the file name itself.               (required)
    "name" - friendly name that's displayed on the dashboard user interface.                  (required)
    "template" - path of the JSP template having place holders for configured components.     (required)

A dashboard can have multiple qualifiers and components, which should be listed under their respective parent elements.

<qualifications>: Parent element for configured qualifiers with an optional attribute and at least one 'qualifier' element.

    "comment" - developer comment on qualification scheme                                     (optional)

<qualifier>: Element to configure a qualifier and has a mandatory attribute,

    "class" - fully qualified name of qualifier class.                                        (required)

The element should have at least one child element as shown below, which is an input to the qualifier.

    <property> - represents an input to the qualifier in the form of key-value pair with attributes,
         "name" - representing key and                                                        (required)
         "value" - representing value.                                                        (required)

[Note: jManage supports custom elements under 'qualifier' element. Though we support custom elements we recommend the usage of 'property' element as we already have base qualifier classes that can parse the element and build an input Map for inheriting qualifier classes.]

<components>: Represents a collection of configured components with at least one component definition element.

<component>: Element to define a dashboard component with following attributes.

    "id" - unique id across all components of a given dashboard configuration file.           (required)
    "class" - fully qualified class name of the dashboard component.                          (required)
    "name" - user friendly name to be displayed on the dashboard user interface.              (optional)
    "refreshInterval" - refreshes the component using AJAX after specified interval of time.  (optional)
    The element should have at least one child element as shown below, which is an input to the component.
    <property> - represents an input to the component in the form of key-value pair with attributes,
         "name" - representing key and                                                        (required)
         "value" - representing value.                                                        (required)

[Note: jManage supports custom elements under 'component' element. Though we support custom elements we recommend the usage of 'property' element as we already have base component classes that can parse the element and build an input Map for inheriting component classes.]

    Place this configuration file under '<jManage-base-dir>/webui/src/main/dashboards' directory.


Create dashboard component class

This is an optional step and can be skipped if the dashboard can be built with the help of out-of-the-box components. There are two ways to implement a custom dashboard component.

1. Implementing DashboardComponent interface.

This approach should be used if custom XML elements are used in dashboard configuration file as input elements. Methods to be implemented are,

    public void init(Element componentConfig) - to be implemented to have the custom logic to parse the XML.
    public String draw(DashboardContext context) - should have the component rendering logic.

Following shows a sample implementation.

public class Graph implements DashboardComponent {
    private String id, name, type;
    private int pollingIntervalInSeconds;
    private List<String> mbeans = new ArrayList<String>();
    private Map<String, List<String>> attributes =
            new HashMap<String, List<String>>();
    private Map<String, List<String>> attributeDisplayNames =
            new HashMap<String, List<String>>();

    /*  Constnats for component config XML element - Begin  */
    protected final String TYPE = "type";
    protected final String POLLING_INTERVAL = "pollingInterval";
    protected final String PARAM = "param";
    protected final String MBEAN = "mbean";
    protected final String ATTRIBUTE = "attribute";
    protected final String DISPLAY_NAME = "displayName";
    /*  Constnats for component config XML element - End    */

    public String getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getType() {
        return type;
    }

    public int getPollingIntervalInSeconds() {
        return pollingIntervalInSeconds;
    }

    @SuppressWarnings("unchecked")
    public void init(Element componentConfig){
        id = componentConfig.getAttribute(ID).getValue();
        name = componentConfig.getAttribute(NAME).getValue();
        type = componentConfig.getChild(TYPE).getTextTrim();
        pollingIntervalInSeconds =
                Integer.parseInt(componentConfig.getChild(POLLING_INTERVAL).getTextTrim());
        List<Element> paramElements = componentConfig.getChildren(PARAM);
        for(Element param: paramElements){
            String mbean = param.getAttribute(MBEAN).getValue();
            String attribute = param.getAttribute(ATTRIBUTE).getValue();
            String attribDisplayName = param.getAttribute(DISPLAY_NAME).getValue();

            if(!mbeans.contains(mbean))
                mbeans.add(mbean);

            if(attributes.containsKey(mbean))
                attributes.get(mbean).add(attribute);
            else{
                List attribs = new ArrayList();
                attribs.add(attribute);
                attributes.put(mbean, attribs);
            }

            if(attributeDisplayNames.containsKey(mbean))
                attributeDisplayNames.get(mbean).add(attribDisplayName);
            else{
                List attribDisplayNames = new ArrayList();
                attribDisplayNames.add(attribDisplayName);
                attributeDisplayNames.put(mbean, attribDisplayNames);
            }
        }
    }

    /**
     * All component developers should be responsible for rendering the component
     *
     * @param context
     * @return String representaion of HTML content that renders the component.
     */
    public String draw(DashboardContext context) {
        ApplicationConfig appConfig = context.getWebContext().getApplicationConfig();
        assert appConfig != null: "No application context present";
        try{
        StringBuffer graphComponent = new StringBuffer().append(
                "<applet code=\"org/jmanage/webui/applets/GraphApplet.class\"").append(
                " width=\"{0}\" height=\"{1}\"").append(
                " archive=\"/applets/applets.jar,/applets/jfreechart-0.9.20.jar,").append(
                "/applets/jcommon-0.9.5.jar\" >").append(
                "<param name=\"graphTitle\" value=\"" getName() "\"></param>").append(
                "<param name=\"pollingInterval\" value=\"" getPollingIntervalInSeconds() "\"></param>").append(
                "<param name=\"remoteURL\" value=\"" context.getServerPath() "/app/fetchAttributeValues.do;jsessionid={2}\"></param>").append(
                "<param name=\"displayNames\" value=\"").append(getAttributeDisplayNamesForGraph()).append("\"></param>").append(
                "<param name=\"attributes\" value=\"").append(getAttributesForGraph(appConfig.getName())).append("\"></param>").append(
                "<param value=\"\" name=\"yAxisLabel\"></param>").append("</applet>");
        return graphComponent.toString();
        }catch(Exception e){
            return "Failure rendering component";
        }
    }


    /**
     *
     * @param applicationName
     * @return
     * @throws Exception
     */
    private String getAttributesForGraph(String applicationName) throws Exception{
	/* custom implementation */
    }

    /**
     *
     * @return
     * @throws Exception
     */
    private String getAttributeDisplayNamesForGraph() throws Exception{
	/* custom implementation */
    }
}

2. Extending from BaseDashboardComponent.

This approach should be used if one is using 'property' elements as input elements. The two methods to be implemented are,

    protected abstract void init(Map<String, String> properties) - provides inputs wrapped inside a Map, should be implemented 
    to have access to the input values.
    protected abstract void drawInternal(DashboardContext context, StringBuffer output) - method should be implemented to have 
    the component rendering logic.

Following code shows a sample implementation.

public class MBeanAttributeValue extends BaseDashboardComponent{
    
    private ObjectName objectName;
    private String attribute;
    private String displayName;

    /**
     *
     * @param properties
     */
    public void init(Map<String, String> properties) {
        try {
            objectName = new ObjectName(properties.get(MBEAN));
            attribute = properties.get(ATTRIBUTE);
            displayName = properties.get(DISPLAY_NAME);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     *
     * @param context
     */
    protected void drawInternal(DashboardContext context, StringBuffer output) {
        ServerConnection connection = context.getWebContext().getServerConnection();
        ObjectAttribute attributeValue =
                (ObjectAttribute)connection.getAttributes(objectName, new String[]{attribute}).get(0);

        String data= displayName != null