/*
* Copyright (C) 2008 Jive Software. All rights reserved.
*
* Licensed 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.jivesoftware.openfire.reporting.graph;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jfree.chart.JFreeChart;
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.archive.MonitoringConstants;
import org.jivesoftware.openfire.plugin.MonitoringPlugin;
import org.jivesoftware.openfire.reporting.stats.StatsViewer;
import org.jivesoftware.openfire.stats.Statistic;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.ParamUtils;
import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.FontFactory;
import com.lowagie.text.Image;
import com.lowagie.text.PageSize;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.DefaultFontMapper;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
/**
*/
public class GraphServlet extends HttpServlet {
private GraphEngine graphEngine;
private StatsViewer statsViewer;
@Override
public void init() throws ServletException {
// load dependencies
MonitoringPlugin plugin =
(MonitoringPlugin) XMPPServer.getInstance().getPluginManager().getPlugin(MonitoringConstants.NAME);
this.graphEngine = (GraphEngine) plugin.getModule(GraphEngine.class);
this.statsViewer = (StatsViewer)plugin.getModule(StatsViewer.class);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// retrieve parameters
String statisticKey = request.getParameter("stat");
String timePeriod = request.getParameter("timeperiod");
String graphcolor = request.getParameter("color");
boolean sparkLines = request.getParameter("sparkline") != null;
boolean pdfFormat = request.getParameter("pdf") != null;
long[] dateRange = GraphEngine.parseTimePeriod(timePeriod);
int width;
int height;
if (pdfFormat) {
// PDF A4 page = 595 wide - (50px * 2 margins) = 495
width = ParamUtils.getIntParameter(request, "width", 495);
height = ParamUtils.getIntParameter(request, "height", 252);
JFreeChart[] charts;
Statistic[] stats;
if (request.getParameter("pdf").equalsIgnoreCase("all")) {
String[] statKeys = statsViewer.getAllHighLevelStatKeys();
List<String> statList = Arrays.asList(statKeys);
Collections.sort(statList, new Comparator<String>() {
public int compare(String stat1, String stat2) {
String statName1 = statsViewer.getStatistic(stat1)[0].getName();
String statName2 = statsViewer.getStatistic(stat2)[0].getName();
return statName1.toLowerCase().compareTo(statName2.toLowerCase());
}
});
charts = new JFreeChart[statList.size()];
stats = new Statistic[statList.size()];
int index = 0;
for (String statName : statList) {
stats[index] = statsViewer.getStatistic(statName)[0];
charts[index] = graphEngine.generateChart(statName, width, height, graphcolor, dateRange[0], dateRange[1], (int)dateRange[2]);
index++;
}
} else {
charts = new JFreeChart[] {graphEngine.generateChart(statisticKey, width, height, graphcolor, dateRange[0], dateRange[1], (int)dateRange[2])};
stats = new Statistic[] {statsViewer.getStatistic(statisticKey)[0]};
}
writePDFContent(request, response, charts, stats, dateRange[0], dateRange[1], width, height);
} else {
byte[] chart;
if (sparkLines) {
width = ParamUtils.getIntParameter(request, "width", 200);
height = ParamUtils.getIntParameter(request, "height", 50);
chart = graphEngine.generateSparklinesGraph(statisticKey, width, height, graphcolor, dateRange[0], dateRange[1], (int)dateRange[2]);
}
else {
width = ParamUtils.getIntParameter(request, "width", 590);
height = ParamUtils.getIntParameter(request, "height", 300);
chart = graphEngine.generateGraph(statisticKey, width, height, graphcolor, dateRange[0], dateRange[1], (int)dateRange[2]);
}
writeImageContent(response, chart, "image/png");
}
}
private void writePDFContent(HttpServletRequest request, HttpServletResponse response, JFreeChart charts[], Statistic[] stats, long starttime, long endtime, int width, int height)
throws IOException
{
try {
Document document = new Document(PageSize.A4, 50, 50, 50, 50);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter writer = PdfWriter.getInstance(document, baos);
writer.setPageEvent(new PDFEventListener(request));
document.open();
int index = 0;
int chapIndex = 0;
for (Statistic stat : stats) {
String serverName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
String dateName = JiveGlobals.formatDate(new Date(starttime)) + " - " +
JiveGlobals.formatDate(new Date(endtime));
Paragraph paragraph = new Paragraph(serverName,
FontFactory.getFont(FontFactory.HELVETICA,
18, Font.BOLD));
document.add(paragraph);
paragraph = new Paragraph(dateName,
FontFactory.getFont(FontFactory.HELVETICA,
14, Font.PLAIN));
document.add(paragraph);
document.add(Chunk.NEWLINE);
document.add(Chunk.NEWLINE);
Paragraph chapterTitle = new Paragraph(++chapIndex + ". " + stat.getName(),
FontFactory.getFont(FontFactory.HELVETICA, 16,
Font.BOLD));
document.add(chapterTitle);
// total hack: no idea what tags people are going to use in the description
// possibly recommend that we only use a <p> tag?
String[] paragraphs = stat.getDescription().split("<p>");
for (String s : paragraphs) {
Paragraph p = new Paragraph(s);
document.add(p);
}
document.add(Chunk.NEWLINE);
PdfContentByte contentByte = writer.getDirectContent();
PdfTemplate template = contentByte.createTemplate(width, height);
Graphics2D graphs2D = template.createGraphics(width, height, new DefaultFontMapper());
Rectangle2D rectangle2D = new Rectangle2D.Double(0, 0, width, height);
charts[index++].draw(graphs2D, rectangle2D);
graphs2D.dispose();
float x = (document.getPageSize().width() / 2) - (width / 2);
contentByte.addTemplate(template, x, writer.getVerticalPosition(true) - height);
document.newPage();
}
document.close();
// setting some response headers
response.setHeader("Expires", "0");
response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
response.setHeader("Pragma", "public");
// setting the content type
response.setContentType("application/pdf");
// the contentlength is needed for MSIE!!!
response.setContentLength(baos.size());
// write ByteArrayOutputStream to the ServletOutputStream
ServletOutputStream out = response.getOutputStream();
baos.writeTo(out);
out.flush();
} catch (DocumentException e) {
Log.error("error creating PDF document: " + e.getMessage());
}
}
private static void writeImageContent(HttpServletResponse response, byte[] imageData, String contentType)
throws IOException
{
ServletOutputStream os = response.getOutputStream();
response.setContentType(contentType);
os.write(imageData);
os.flush();
os.close();
}
class PDFEventListener extends PdfPageEventHelper {
private HttpServletRequest request;
public PDFEventListener(HttpServletRequest request) {
this.request = request;
}
@Override
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte cb = writer.getDirectContent();
try {
cb.setColorStroke(new Color(156,156,156));
cb.setLineWidth(2);
cb.moveTo(document.leftMargin(), document.bottomMargin() + 32);
cb.lineTo(document.getPageSize().width() - document.rightMargin(), document.bottomMargin() + 32);
cb.stroke();
Image gif = Image.getInstance("http://" + request.getServerName() +
":" + request.getServerPort() + "/plugins/"+MonitoringConstants.NAME+"/images/pdf_generatedbyof.gif");
cb.addImage(gif, 221, 0, 0, 28, (int)document.leftMargin(), (int)document.bottomMargin());
} catch (Exception e) {
Log.error("error drawing PDF footer: " + e.getMessage());
}
cb.saveState();
}
}
}