/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004, 2005, 2006], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program is distributed * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.ui.servlet; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.appdef.shared.AppdefEntityID; import org.hyperic.hq.appdef.shared.AppdefEntityNotFoundException; import org.hyperic.hq.auth.shared.SessionNotFoundException; import org.hyperic.hq.auth.shared.SessionTimeoutException; import org.hyperic.hq.authz.shared.PermissionException; import org.hyperic.hq.bizapp.shared.EventLogBoss; import org.hyperic.hq.bizapp.shared.MeasurementBoss; import org.hyperic.hq.context.Bootstrap; import org.hyperic.hq.control.ControlEvent; import org.hyperic.hq.events.server.session.EventLog; import org.hyperic.hq.measurement.MeasurementNotFoundException; import org.hyperic.hq.ui.Constants; import org.hyperic.hq.ui.beans.ChartDataBean; import org.hyperic.hq.ui.util.RequestUtils; import org.hyperic.image.chart.Chart; import org.hyperic.image.chart.ColumnChart; import org.hyperic.image.chart.DataPointCollection; import org.hyperic.image.chart.EventPointCollection; import org.hyperic.image.chart.LineChart; import org.hyperic.image.chart.VerticalChart; import org.hyperic.util.TimeUtil; import org.hyperic.util.pager.PageControl; import org.hyperic.util.pager.PageList; /** * <p>Extends ChartServlet to graph one or more metrics. By default, * <code>showPeak</code>, <code>showHighRange</code>, * <code>showValues</code>, <code>showAverage</code>, * <code>showLowRange</code>, <code>showLow</code> and * <code>showBaseline</code> are all true.</p> * * <p>Additional parameters are as follows (any required parameters * are in <i>italics</i>):</p> * * <table border="1"> * <tr><th> key </th><th> value </th></tr> * <tr><td> <i>chartDataKey</i> </td><td> <string> </td></tr> * <tr><td> showEvents </td><td> (<b>false</b> | true) </td></tr> * </table> * * <p>The <code>chartDataKey</code> will be used to retrieve the chart * data from the session. Once it is pulled, it will be removed from * the session.</p> * */ public class MetricChartServlet extends VerticalChartServlet { /** * Request parameter for the chart data key session attribute. */ public static final String CHART_DATA_KEY_PARAM = "chartDataKey"; /** Request parameter for whether or not to show control actions. */ public static final String SHOW_EVENTS_PARAM = "showEvents"; // member data private Log log = LogFactory.getLog( MetricChartServlet.class.getName() ); public MetricChartServlet () {} /** * Create the image being rendered. * * @param request the servlet request */ protected Object createImage(HttpServletRequest request) throws ServletException { ChartDataBean dataBean; try { dataBean = getupMetricData(request); } catch (Exception e) { throw new ServletException(e); } // initialize the chart Chart chart = createChart(request, dataBean); initializeChart(chart, request); // the subclass is responsible for plotting the data log.debug("Plotting data."); plotData(request, chart, dataBean); return chart; } /** * Create and return the chart. This method will be called after * the parameters have been parsed. * * @return the newly created chart */ protected Chart createChart(HttpServletRequest request, ChartDataBean dataBean) { // We will actually set a flag here to determine whether we // should draw a LineChart or a column chart. If we are // charting just one set of data / event points, we'll plot a // ColumnChart. Otherwise we'll plot a LineChart. List dataPointsList = dataBean.getDataPoints(); boolean plotLineChart = (dataPointsList.size() > 1); if (plotLineChart) { log.trace("plotting a line chart"); return new LineChart(getImageWidth(request), getImageHeight(request)); } else { log.trace("plotting a column chart"); return new ColumnChart(getImageWidth(request), getImageHeight(request)); } } /** * Initialize the chart. This method will be called after the * parameters have been parsed and the chart has been created. * * @param chart the chart */ protected void initializeChart(Chart chart, HttpServletRequest request) { super.initializeChart(chart, request); // chart flags boolean showEvents = parseBooleanParameter( request, SHOW_EVENTS_PARAM, getDefaultShowEvents() ); VerticalChart verticalChart = (VerticalChart) chart; verticalChart.showEvents = showEvents; verticalChart.showRightLabels = false; verticalChart.rightLabelWidth = (int) (getImageWidth(request) * 0.1); verticalChart.xLabelsSkip = 5; } /** * This method will be called automatically by the ChartServlet. * It should handle adding data to the chart, setting up the X and * Y axis labels, etc. * * @param request the HTTP request */ protected void plotData(HttpServletRequest request, Chart chart, ChartDataBean dataBean) throws ServletException { VerticalChart veritcalChart = (VerticalChart) chart; List dataPointsList = dataBean.getDataPoints(); List eventsPointsList = dataBean.getEventPoints(); // make sure they're the same size if (dataPointsList.size() == eventsPointsList.size()) { if (log.isDebugEnabled()) { log.debug("got " + dataPointsList.size() + " set(s) of data / event points."); } } else { throw new ServletException( "Number of data point sets and number of event point sets must be the same."); } veritcalChart.setNumberDataSets(dataPointsList.size()); int i = 0; Iterator it = dataPointsList.iterator(); Iterator jt = eventsPointsList.iterator(); while (it.hasNext() && jt.hasNext()) { // data points List data = (List) it.next(); log.trace("plotting " + data.size() + " data points"); DataPointCollection chartData = chart.getDataPoints(i); chartData.addAll(data); // events List events = (List) jt.next(); log.trace("plotting " + events.size() + " event points"); EventPointCollection chartEvents = chart.getEventPoints(i); chartEvents.addAll(events); // increment ++i; } } /** * Return the default <code>showPeak</code>. */ protected boolean getDefaultShowPeak() { return true; } /** * Return the default <code>showHighRange</code>. */ protected boolean getDefaultShowHighRange() { return true; } /** * Return the default <code>showValues</code>. */ protected boolean getDefaultShowValues() { return true; } /** * Return the default <code>showAverage</code>. */ protected boolean getDefaultShowAverage() { return true; } /** * Return the default <code>showLowRange</code>. */ protected boolean getDefaultShowLowRange() { return true; } /** * Return the default <code>showLow</code>. */ protected boolean getDefaultShowLow() { return true; } /** * Return the default <code>showBaseline</code>. */ protected boolean getDefaultShowBaseline() { return true; } /** * Return the default <code>showEvents</code>. */ protected boolean getDefaultShowEvents() { return true; } protected ChartDataBean getupMetricData(HttpServletRequest request) throws SessionNotFoundException, SessionTimeoutException, MeasurementNotFoundException, RemoteException, AppdefEntityNotFoundException, PermissionException, ServletException { int sessionId = RequestUtils.getSessionId(request).intValue(); MeasurementBoss mb = Bootstrap.getBean(MeasurementBoss.class); EventLogBoss eb = Bootstrap.getBean(EventLogBoss.class); String[] eids = request.getParameterValues( Constants.ENTITY_ID_PARAM ); if(eids == null){ return new ChartDataBean(new ArrayList(), new ArrayList()); } AppdefEntityID[] resources = new AppdefEntityID[eids.length]; for (int i = 0; i < eids.length; i++) { resources[i] = new AppdefEntityID(eids[i]); } // Get data for charts and put it in session. In reality only // one of either resources or metrics can have more than one // entry, so it's really not as much of a nested loop as it // seems. However, the code is written this way so that it // can be used in both the multi-resource and the multi-metric // case. // data points for chart Integer mid = new Integer(request.getParameter("m")); long startDate = Long.parseLong(request.getParameter("start")); long endDate = Long.parseLong(request.getParameter("end")); // Use the current time concatenated with metric // template id for key. List dataPointsList = new ArrayList(resources.length); List eventPointsList = new ArrayList(resources.length); for (int j = 0; j < resources.length; ++j) { if (log.isDebugEnabled()) { log.debug("mtid=" + mid + ", rid=" + resources[j].getId()); log.debug("startDate=" + startDate); log.debug("endDate=" + endDate); } long interval = TimeUtil .getInterval(startDate, endDate, Constants.DEFAULT_CHART_POINTS); if (interval > 0) { try { PageList data = mb.findMeasurementData ( sessionId, mid, resources[j], startDate, endDate, interval, true, PageControl.PAGE_ALL ); if ( log.isDebugEnabled() ) { log.debug("Found " + data.size() + " datapoints."); if ( log.isTraceEnabled() ) { log.trace("data: " + data); } } dataPointsList.add(data); } catch (MeasurementNotFoundException e) { dataPointsList.add(new PageList()); } List controlActions = eb.getEvents(sessionId, ControlEvent.class.getName(), resources[j], startDate, endDate); // We need to make sure that the event IDs get set // for the legend. int k = 0; for (Iterator it = controlActions.iterator(); it.hasNext();) { EventLog event = (EventLog) it.next(); event.setEventID(++k); } eventPointsList.add(controlActions); } } return new ChartDataBean(dataPointsList, eventPointsList); } } // EOF