View Javadoc
1   /*
2    * Licensed under the GPL License. You may not use this file except in compliance with the License.
3    * You may obtain a copy of the License at
4    *
5    *   https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
6    *
7    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
8    * WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
9    * PURPOSE.
10   */
11  package psiprobe.controllers;
12  
13  import jakarta.inject.Inject;
14  import jakarta.servlet.http.HttpServletRequest;
15  import jakarta.servlet.http.HttpServletResponse;
16  
17  import java.awt.BasicStroke;
18  import java.awt.Color;
19  
20  import org.jfree.chart.ChartFactory;
21  import org.jfree.chart.ChartUtils;
22  import org.jfree.chart.JFreeChart;
23  import org.jfree.chart.axis.DateAxis;
24  import org.jfree.chart.plot.PlotOrientation;
25  import org.jfree.chart.renderer.xy.XYAreaRenderer;
26  import org.jfree.chart.ui.RectangleInsets;
27  import org.jfree.data.xy.DefaultTableXYDataset;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  import org.springframework.stereotype.Controller;
31  import org.springframework.web.bind.ServletRequestUtils;
32  import org.springframework.web.bind.annotation.RequestMapping;
33  import org.springframework.web.servlet.ModelAndView;
34  import org.springframework.web.servlet.mvc.AbstractController;
35  
36  import psiprobe.Utils;
37  import psiprobe.beans.stats.providers.SeriesProvider;
38  import psiprobe.jfreechart.XYLine3DRenderer;
39  import psiprobe.model.stats.StatsCollection;
40  
41  /**
42   * Plots data from "statsCollection" bean. The data is converted to XYSeries using SeriesProvider,
43   * name of which would be passed as a request parameter. The servlet can only plot up to 9 series.
44   * It is customizable using these request parameters:
45   * <ul>
46   * <li>s1c, s2c, ... s9c - Series #i main color</li>
47   * <li>s1o, s2o, ... s9o - Series #i outline color</li>
48   * <li>bc - Chart background color</li>
49   * <li>gc - Chart grid lines color</li>
50   * <li>xl - X axis label</li>
51   * <li>yl - Y axis label</li>
52   * <li>xz - image width</li>
53   * <li>yx - image height</li>
54   * <li>l - show legend (boolean: true|false)</li>
55   * <li>p - name of series provider bean</li>
56   * </ul>
57   */
58  @Controller
59  public class RenderChartController extends AbstractController {
60  
61    /** The Constant logger. */
62    private static final Logger logger = LoggerFactory.getLogger(RenderChartController.class);
63  
64    /** The stats collection. */
65    @Inject
66    private StatsCollection statsCollection;
67  
68    /**
69     * Gets the stats collection.
70     *
71     * @return the stats collection
72     */
73    public StatsCollection getStatsCollection() {
74      return statsCollection;
75    }
76  
77    /**
78     * Sets the stats collection.
79     *
80     * @param statsCollection the new stats collection
81     */
82    public void setStatsCollection(StatsCollection statsCollection) {
83      this.statsCollection = statsCollection;
84    }
85  
86    @RequestMapping(path = "/chart.png")
87    @Override
88    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
89        throws Exception {
90      return super.handleRequest(request, response);
91    }
92  
93    @Override
94    protected ModelAndView handleRequestInternal(HttpServletRequest request,
95        HttpServletResponse response) throws Exception {
96  
97      // the max number of series
98      final int seriesMaxCount = 9;
99  
100     // get Series Color from the request
101     int[] seriesColor = new int[seriesMaxCount];
102     seriesColor[0] = Utils.toIntHex(request.getParameter("s1c"), 0x9bd2fb);
103     seriesColor[1] = Utils.toIntHex(request.getParameter("s2c"), 0xFF0606);
104     for (int i = 2; i < seriesMaxCount; i++) {
105       seriesColor[i] = Utils.toIntHex(request.getParameter("s" + (i + 1) + "c"), -1);
106     }
107 
108     // get Series Outline Color from the request
109     int[] seriesOutlineColor = new int[seriesMaxCount];
110     seriesOutlineColor[0] = Utils.toIntHex(request.getParameter("s1o"), 0x0665aa);
111     seriesOutlineColor[1] = Utils.toIntHex(request.getParameter("s2o"), 0x9d0000);
112     for (int i = 2; i < seriesMaxCount; i++) {
113       seriesOutlineColor[i] = Utils.toIntHex(request.getParameter("s" + (i + 1) + "o"), -1);
114     }
115 
116     // background color
117     int backgroundColor = Utils.toIntHex(request.getParameter("bc"), 0xFFFFFF);
118 
119     // grid color
120     int gridColor = Utils.toIntHex(request.getParameter("gc"), 0);
121 
122     // X axis title
123     String labelX = ServletRequestUtils.getStringParameter(request, "xl", "");
124 
125     // Y axis title
126     String labelY = ServletRequestUtils.getStringParameter(request, "yl", "");
127 
128     // image width
129     int width = ServletRequestUtils.getIntParameter(request, "xz", 800);
130 
131     // image height
132     int height = ServletRequestUtils.getIntParameter(request, "yz", 400);
133 
134     // show legend?
135     boolean showLegend = ServletRequestUtils.getBooleanParameter(request, "l", true);
136 
137     // Series provider
138     String provider = ServletRequestUtils.getStringParameter(request, "p");
139 
140     // Chart type
141     String chartType = ServletRequestUtils.getStringParameter(request, "ct", "area");
142 
143     DefaultTableXYDataset ds = new DefaultTableXYDataset();
144 
145     if (provider != null) {
146       Object series = getApplicationContext().getBean(provider);
147       if (series instanceof SeriesProvider) {
148         ((SeriesProvider) series).populate(ds, statsCollection, request);
149       } else {
150         logger.error("SeriesProvider '{}' does not implement '{}'", provider, SeriesProvider.class);
151       }
152     }
153 
154     // Build series data from the give statistic
155     JFreeChart chart = null;
156     if ("area".equals(chartType)) {
157       chart = ChartFactory.createXYAreaChart("", labelX, labelY, ds, PlotOrientation.VERTICAL,
158           showLegend, false, false);
159 
160       ((XYAreaRenderer) chart.getXYPlot().getRenderer()).setOutline(true);
161 
162     } else if ("stacked".equals(chartType)) {
163       chart = ChartFactory.createStackedXYAreaChart("", labelX, labelY, ds,
164           PlotOrientation.VERTICAL, showLegend, false, false);
165 
166     } else if ("line".equals(chartType)) {
167       chart = ChartFactory.createXYLineChart("", labelX, labelY, ds, PlotOrientation.VERTICAL,
168           showLegend, false, false);
169 
170       final XYLine3DRenderer renderer = new XYLine3DRenderer();
171       renderer.setDrawOutlines(true);
172       for (int i = 0; i < seriesMaxCount; i++) {
173         renderer.setSeriesLinesVisible(i, true);
174         renderer.setSeriesShapesVisible(i, true);
175         renderer.setSeriesStroke(i, new BasicStroke(2));
176       }
177       renderer.setXOffset(1);
178       renderer.setYOffset(1);
179       chart.getXYPlot().setRenderer(renderer);
180     }
181 
182     if (chart != null) {
183       chart.setAntiAlias(true);
184       chart.setBackgroundPaint(new Color(backgroundColor));
185       for (int i = 0; i < seriesMaxCount; i++) {
186         if (seriesColor[i] >= 0) {
187           chart.getXYPlot().getRenderer().setSeriesPaint(i, new Color(seriesColor[i]));
188         }
189         if (seriesOutlineColor[i] >= 0) {
190           chart.getXYPlot().getRenderer().setSeriesOutlinePaint(i,
191               new Color(seriesOutlineColor[i]));
192         }
193       }
194       chart.getXYPlot().setDomainGridlinePaint(new Color(gridColor));
195       chart.getXYPlot().setRangeGridlinePaint(new Color(gridColor));
196       chart.getXYPlot().setDomainAxis(0, new DateAxis());
197       chart.getXYPlot().setDomainAxis(1, new DateAxis());
198       chart.getXYPlot().setInsets(new RectangleInsets(-15, 0, 0, 10));
199 
200       response.setHeader("Content-type", "image/png");
201       response.getOutputStream()
202           .write(ChartUtils.encodeAsPNG(chart.createBufferedImage(width, height)));
203     }
204 
205     return null;
206   }
207 }