/** * Copyright 2012 Comcast Corporation * * 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 com.comcast.cmb.common.controller; import com.comcast.cmb.common.util.Util; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.PlotOrientation; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.chart.ChartUtilities; import org.json.JSONObject; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Vector; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** * Test servlet for endpoint * @author bwolf, michael * */ public class EndpointServlet extends HttpServlet { private static final long serialVersionUID = 1L; // keeps last n messages per endpoint private static ConcurrentHashMap<String, List<EndpointMessage>> messageMap = new ConcurrentHashMap<String, List<EndpointMessage>>(); // keeps endpoint stats private static ConcurrentHashMap<String, EndpointMetrics> metricsMap = new ConcurrentHashMap<String, EndpointMetrics>(); // keeps config information to model certain error behavior //private static Map<String, EndpointFailureConfiguration> failureConfigMap = new ConcurrentHashMap<String, EndpointFailureConfiguration>(); // for each endpoint message a set of message hashes to help identify duplicates private static ConcurrentHashMap<String, Map<String, Boolean>> messageHashes = new ConcurrentHashMap<String, Map<String, Boolean>>(); // for each endpoint keeps number of duplciates received (no record = no dups, 1 = 1 dup, ...) private static ConcurrentHashMap<String, AtomicInteger> messageDuplicates = new ConcurrentHashMap<String, AtomicInteger>(); private static AtomicInteger globalMessageCounter = new AtomicInteger(0); private final static int MAX_MSG_PER_USER = 100; private final static String VERSION = CMBControllerServlet.VERSION; private static Logger logger = Logger.getLogger(EndpointServlet.class); private static final Random rand = new Random(); /*private class EndpointFailureConfiguration { public int numFailuresBeforeSuccess = 10; public int currentFailureCounter = 0; public int httpErrorCode = 404; }*/ private class EndpointMessage { public String host; public String id; public String recvOn; public String url; public String method; public String msg; public String httpHeader; //public boolean failure = false; } private class EndpointMetrics { public EndpointMetrics(EndpointMessage msg) { startTime = new Date(); endTime = new Date(); lastMessageTime = new Date(); messageCount = 1; minWaveLength = Long.MAX_VALUE; minLatency = Long.MAX_VALUE; lastMessage = msg; } public Date startTime; public Date endTime; public long messageCount; public long minWaveLength; public long maxWaveLength; public long averageWaveLength; public long totalWaveLength; public long lastWaveLength; public long messagesPerSecond; public long minLatency; public long maxLatency; public long averageLatency; public long totalLatency; public long lastLatency; public Date lastMessageTime; public EndpointMessage lastMessage; public List<Integer> timeSeries = new ArrayList<Integer>(); } private String hashMessage(String message) { try { MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(message.getBytes("UTF-8")); byte[] hash = digest.digest(); return Base64.encodeBase64URLSafeString(hash); } catch (Exception e) { logger.error("event=hashing_failed", e); } return null; } @Override public void init() throws ServletException { super.init(); try { Util.initLog4jTest(); } catch (Exception e) { throw new ServletException(e); } } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String pathInfo = request.getPathInfo(); if (pathInfo == null) { doHelp(request, response); return; } //logger.info("pathInfo="+pathInfo+" ip="+request.getRemoteAddr()); if (pathInfo.toLowerCase().startsWith("/recv")) { doReceiveMessage(request, response); } else if (pathInfo.toLowerCase().startsWith("/help")) { doHelp(request, response); //} else if (pathInfo.toLowerCase().startsWith("/config")) { //doConfig(request, response); } else if (pathInfo.toLowerCase().startsWith("/clear")) { doClear(request, response); } else if (pathInfo.toLowerCase().startsWith("/nop")) { doAlmostNothing(request, response); } else if (pathInfo.toLowerCase().startsWith("/log")) { doLogMessage(request, response); } else if (pathInfo.toLowerCase().startsWith("/info")) { doDisplayMessages(request, response); } else if (pathInfo.toLowerCase().startsWith("/token")) { doGetToken(request, response); } else if (pathInfo.toLowerCase().startsWith("/metrics")) { doMetrics(request, response); } else if (pathInfo.toLowerCase().startsWith("/chart")) { doChart(request, response); } else if (pathInfo.toLowerCase().startsWith("/list")) { doList(request, response); } else { doHelp(request, response); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String pathInfo = request.getPathInfo(); if (pathInfo == null) { doHelp(request, response); return; } //logger.info("pathInfo="+pathInfo+" ip="+request.getRemoteAddr()); if (pathInfo.toLowerCase().startsWith("/recv")) { doReceiveMessage(request, response); } else if (pathInfo.toLowerCase().startsWith("/help")) { doHelp(request, response); //} else if (pathInfo.toLowerCase().startsWith("/config")) { //doConfig(request, response); } else if (pathInfo.toLowerCase().startsWith("/clear")) { doClear(request, response); } else if (pathInfo.toLowerCase().startsWith("/nop")) { doAlmostNothing(request, response); } else if (pathInfo.toLowerCase().startsWith("/log")) { doLogMessage(request, response); } else if (pathInfo.toLowerCase().startsWith("/info")) { doDisplayMessages(request, response); } else if (pathInfo.toLowerCase().startsWith("/token")) { doGetToken(request, response); } else if (pathInfo.toLowerCase().startsWith("/metrics")) { doMetrics(request, response); } else if (pathInfo.toLowerCase().startsWith("/chart")) { doChart(request, response); } else if (pathInfo.toLowerCase().startsWith("/list")) { doList(request, response); } else { doHelp(request, response); } } protected void doHelp(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { StringBuffer sb = new StringBuffer(); sb.append("<h3>EndPoint Help - V " + VERSION + "</h3>"); sb.append("<b>/recv/id</b> - receive message and calculate metrics on the fly by test id (can be used for up to 2000 msg/sec)<br/>"); sb.append("<b>/recv/id?errorCode=<errorCode>&chance=<0-1></b> - occasionally respond with an error<br/>"); sb.append("<b>/recv/id?delayMS=<delayMS>&chance=<0-1></b> - occasionally respond with a delay<br/>"); sb.append("<b>/recv/id?numResponseBytes=<numResponseBytes>&chance=<0-1></b> - occasionally respond with an error<br/>"); sb.append("<b>/log/id</b> - receive and do basic logging for message by test id<br/>"); sb.append("<b>/nop/id</b> - just receive and globally count messages by test id<br/>"); sb.append("<b><a href=\"clear\">/clear</a></b> - clear all messages, logs and state information<br/>"); sb.append("<b>/info/id</b> - list recent messages returning http 200 by test id<br/>"); sb.append("<b>/token/id</b> - obtain sns token by test id<br/>"); sb.append("<b>/metrics/id</b> - list metrics for test by id<br/>"); sb.append("<b>/chart/id</b> - render chart for test by id<br/>"); sb.append("<b><a href=\"list\">/list</a></b> - list information for all test runs<br/>"); sb.append("<b>/help</b> - this help<br/>"); sb.append("<b>/config/id?errorCount=10&statusCode=404</b> - set number of failures and error code before successful receipt of messages begins by test id<br/>"); doOutput(200, response, "Help", sb.toString()); } /*protected void doConfig(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String pathInfo = request.getPathInfo(); String id = pathInfo.substring(pathInfo.lastIndexOf("/")+1); EndpointFailureConfiguration failureConfig = new EndpointFailureConfiguration(); failureConfigMap.put(id, failureConfig); if (request.getParameter("errorCount") != null) { failureConfig.numFailuresBeforeSuccess = Integer.parseInt(request.getParameter("errorCount")); } if (request.getParameter("statusCode") != null) { failureConfig.httpErrorCode = Integer.parseInt(request.getParameter("statusCode")); } doRawOutput(200, response, "OK"); }*/ protected void doClear(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { messageMap = new ConcurrentHashMap<String, List<EndpointMessage>>(); metricsMap = new ConcurrentHashMap<String, EndpointMetrics>(); //failureConfigMap = new ConcurrentHashMap<String, EndpointFailureConfiguration>(); globalMessageCounter = new AtomicInteger(0); messageHashes = new ConcurrentHashMap<String, Map<String, Boolean>>(); messageDuplicates = new ConcurrentHashMap<String, AtomicInteger>(); doRawOutput(200, response, "OK"); } protected void doAlmostNothing(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { globalMessageCounter.incrementAndGet(); doRawOutput(200, response, "OK"); } protected void doLogMessage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { globalMessageCounter.incrementAndGet(); String msg=parseMessage(request).msg; if (msg.length()>30){ msg=msg.substring(0,29); } logger.info("event=Endpoint_dolog_Message url="+request.getRequestURI()+ " msg="+msg+"..." + " ts="+System.currentTimeMillis()); doRawOutput(200, response, "OK"); } protected void doGetToken(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String pathInfo = request.getPathInfo(); String id = pathInfo.substring(pathInfo.lastIndexOf("/")+1); if (id.toLowerCase().startsWith("token")) { doOutput(404, response, "EndPoint - GetToken", "Please provide an id"); return; } List<EndpointMessage> messages = getMessages(id); if (messages == null || messages.size() == 0) { doOutput(404, response, "EndPoint - GetToken", "Cannot find token for id="+id); return; } for (EndpointMessage message : messages) { try { JSONObject json = new JSONObject(message.msg); String token = json.getString("Token"); if (token == null) { continue; } doOutput(200, response, "EndPoint - GetToken", token); return; } catch (Exception ex) { continue; } } doOutput(404, response, "EndPoint - GetToken", "Cannot find token for id="+id); } private synchronized void addMessage(EndpointMessage msg) { // add message messageMap.putIfAbsent(msg.id, new Vector<EndpointMessage>(MAX_MSG_PER_USER)); List<EndpointMessage> messages = messageMap.get(msg.id); if (messages.size() == MAX_MSG_PER_USER) { messages.remove(MAX_MSG_PER_USER - 1); //remove the oldest message } messages.add(0, msg); // add metrics metricsMap.putIfAbsent(msg.id, new EndpointMetrics(msg)); EndpointMetrics metrics = metricsMap.get(msg.id); metrics.endTime = new Date(); metrics.messageCount++; metrics.lastWaveLength = metrics.endTime.getTime() - metrics.lastMessageTime.getTime(); metrics.lastMessageTime = metrics.endTime; metrics.lastMessage = msg; metrics.totalWaveLength += metrics.lastWaveLength; if (metrics.lastWaveLength < metrics.minWaveLength) { metrics.minWaveLength = metrics.lastWaveLength; } if (metrics.lastWaveLength > metrics.maxWaveLength) { metrics.maxWaveLength = metrics.lastWaveLength; } if (metrics.messageCount > 1) { metrics.averageWaveLength = metrics.totalWaveLength / (metrics.messageCount - 1); } if (metrics.endTime.getTime() > metrics.startTime.getTime()) { metrics.messagesPerSecond = 1000 * metrics.messageCount / (metrics.endTime.getTime() - metrics.startTime.getTime()); } String tokens[] = msg.msg.split(";"); if (tokens.length >= 2) { try { Date sentTime = new Date(Long.parseLong(tokens[1])); metrics.lastLatency = (new Date()).getTime() - sentTime.getTime(); metrics.totalLatency += metrics.lastLatency; } catch (Exception ex) { logger.error("exception", ex); } } if (metrics.messageCount > 0) { metrics.averageLatency = metrics.totalLatency / metrics.messageCount; } if (metrics.lastLatency < metrics.minLatency) { metrics.minLatency = metrics.lastLatency; } if (metrics.lastLatency > metrics.maxLatency) { metrics.maxLatency = metrics.lastLatency; } int idx = (int)(metrics.endTime.getTime() - metrics.startTime.getTime())/1000; while (metrics.timeSeries.size()-1 < idx) { metrics.timeSeries.add(new Integer(0)); } metrics.timeSeries.set(idx, metrics.timeSeries.get(idx) + 1); // check for dups String message = null; if (msg.msg.contains("\"MessageId\":")) { message = msg.msg.substring(0, msg.msg.indexOf("\"MessageId\":")); } else { message = msg.msg; } String messageHash = hashMessage(message); messageHashes.putIfAbsent(msg.id, new ConcurrentHashMap<String, Boolean>()); ConcurrentHashMap<String, Boolean> hashSet = (ConcurrentHashMap<String, Boolean>)messageHashes.get(msg.id); if (hashSet.putIfAbsent(messageHash, true) != null) { if (messageDuplicates.putIfAbsent(msg.id, new AtomicInteger(1)) != null) { messageDuplicates.get(msg.id).incrementAndGet(); } } } protected void doChart(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String pathInfo = request.getPathInfo(); String id = pathInfo.substring(pathInfo.lastIndexOf("/")+1); EndpointMetrics metrics = metricsMap.get(id); if (metrics != null) { byte b[] = generateChart(metrics, id); response.setContentLength(b.length); response.setContentType("image/jpeg"); response.getOutputStream().write(b); response.flushBuffer(); } else { doOutput(404, response, "EndPoint - Chart", "Page not found"); } } protected void doMetrics(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String pathInfo = request.getPathInfo(); String id = pathInfo.substring(pathInfo.lastIndexOf("/")+1); EndpointMetrics metrics = metricsMap.get(id); if (metrics != null) { StringBuilder sb = new StringBuilder(); sb.append("<table>"); sb.append("<tr><td>Start Time:</td><td>" + metrics.startTime + "</td></tr>"); sb.append("<tr><td>End Time:</td><td>" + metrics.endTime + "</td></tr>"); sb.append("<tr><td><b>Messages Per Second:</b></td><td><b>" + metrics.messagesPerSecond + "</b></td></tr>"); sb.append("<tr><td><b>Message Count:</b></td><td><b>" + metrics.messageCount + "</b></td></tr>"); sb.append("<tr><td><b>Avergae Latency:</b></td><td><b>" + metrics.averageLatency + "</b></td></tr>"); sb.append("<tr><td>Last Latency:</td><td>" + metrics.lastLatency + "</td></tr>"); sb.append("<tr><td>Max Latency:</td><td>" + metrics.maxLatency + "</td></tr>"); sb.append("<tr><td>Min Latency:</td><td>" + metrics.minLatency + "</td></tr>"); sb.append("<tr><td>Total Latency:</td><td>" + metrics.totalLatency + "</td></tr>"); sb.append("<tr><td>Avergae Wave Length:</td><td>" + metrics.averageWaveLength + "</td></tr>"); sb.append("<tr><td>Last Wave Length:</td><td>" + metrics.lastWaveLength + "</td></tr>"); sb.append("<tr><td>Max Wave Length:</td><td>" + metrics.maxWaveLength + "</td></tr>"); sb.append("<tr><td>Min Wave Length:</td><td>" + metrics.minWaveLength + "</td></tr>"); sb.append("<tr><td>Total Wave Length:</td><td>" + metrics.totalWaveLength + "</td></tr>"); sb.append("<tr><td>Last Receive Time:</td><td>" + metrics.lastMessageTime + "</td></tr>"); sb.append("<tr><td>Last Messsage:</td><td>" + metrics.lastMessage.msg + "</td></tr>"); sb.append("<tr><td>Time Series:</td><td>" + metrics.timeSeries + "</td></tr>"); sb.append("</table>"); doOutput(200, response, "EndPoint - Metrics", sb.toString()); } else { doOutput(404, response, "EndPoint - Metrics", "Page not found"); } } protected void doList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Iterator<String> iter = metricsMap.keySet().iterator(); StringBuilder sb = new StringBuilder(); if (globalMessageCounter.get() > 0) { sb.append("<b>Global Message Count: " + globalMessageCounter.get() + "</b><br/>"); } if (metricsMap.size() == 0) { sb.append("no messages<br/>"); } else { sb.append("<table>"); sb.append("<tr><th>Row</th><th>ID</th><th>Metrics</th><th>Messages</th><th>Chart</th><th>Message Count</th><th>Last Message Received</th><th>Duplicates</th></tr>"); int rowNum = 1; while (iter.hasNext()) { String id = iter.next(); EndpointMetrics metrics = metricsMap.get(id); sb.append("<tr>"); sb.append("<td>" + rowNum + "</td>"); sb.append("<td>" + id + "</td>"); sb.append("<td><a href=\"metrics/" + id + "\">metrics</a></td>"); sb.append("<td><a href=\"info/" + id + "\">messages</a></td>"); sb.append("<td><a href=\"chart/" + id + "\">chart</a></td>"); sb.append("<td>"+(metrics.messageCount-1)+"</td>"); sb.append("<td>"+metrics.lastMessageTime+"</td>"); sb.append("<td>"+(messageDuplicates.containsKey(id)?messageDuplicates.get(id):"")+"</td></tr>"); rowNum++; } sb.append("</table>"); } doOutput(200, response, "EndPoint - List", sb.toString()); } private List<EndpointMessage> getMessages(String id) { return messageMap.get(id); } private EndpointMessage parseMessage(HttpServletRequest request) throws IOException { EndpointMessage msg = new EndpointMessage(); String pathInfo = request.getPathInfo(); msg.id = pathInfo.substring(pathInfo.lastIndexOf("/")+1); SimpleDateFormat fmt = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); msg.host = request.getRemoteAddr() + "/" + request.getRemoteHost(); msg.recvOn = fmt.format(new Date()); msg.url = request.getRequestURL().toString(); msg.method = request.getMethod(); msg.msg = ""; if (msg.method.equals("POST")) { BufferedReader reader = request.getReader(); String line; while ((line = reader.readLine()) != null) { msg.msg += line; } } return msg; } protected void doReceiveMessage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { globalMessageCounter.incrementAndGet(); EndpointMessage msg = parseMessage(request); // obey error behavior only after first message (typically subscription confirmation request) was received successfully if (request.getParameter("errorCode") != null && messageMap.containsKey(msg.id)) { int errorCode = Integer.parseInt(request.getParameter("errorCode")); double chance = 1.0; if (request.getParameter("chance") != null) { chance = Double.parseDouble(request.getParameter("chance")); } if (rand.nextDouble() <= chance) { doOutput(errorCode, response, "Failed", "Failed"); return; } } if (request.getParameter("delayMS") != null && messageMap.containsKey(msg.id)) { long delayMS = Integer.parseInt(request.getParameter("delayMS")); double chance = 1.0; if (request.getParameter("chance") != null) { chance = Double.parseDouble(request.getParameter("chance")); } if (rand.nextDouble() <= chance) { try { Thread.sleep(delayMS); } catch (Exception ex) { throw new ServletException(ex); } } } if (request.getParameter("numResponseBytes") != null && messageMap.containsKey(msg.id)) { int numResponseBytes = Integer.parseInt(request.getParameter("numResponseBytes")); double chance = 1.0; if (request.getParameter("chance") != null) { chance = Double.parseDouble(request.getParameter("chance")); } if (rand.nextDouble() <= chance) { char[] bytes = new char[numResponseBytes]; Arrays.fill(bytes, 'A'); doRawOutput(200, response, bytes); return; } } /*if (failureConfigMap.containsKey(msg.id)) { EndpointFailureConfiguration failureConfig = failureConfigMap.get(msg.id); logger.info("[doReceiveMessage] currentFailureCounter="+failureConfig.currentFailureCounter+" numFailuresBeforeSuccess="+failureConfig.numFailuresBeforeSuccess); if (failureConfig.currentFailureCounter < failureConfig.numFailuresBeforeSuccess) { failureConfig.currentFailureCounter++; msg.failure = true; logger.info("[doReceiveMessage] event=failedResponse"); doOutput(failureConfig.httpErrorCode, response, "Failed", "Failed"); return; } }*/ Enumeration<String> headerNames = request.getHeaderNames(); String header = ""; while(headerNames.hasMoreElements()) { String headerName = (String)headerNames.nextElement(); header += headerName + ": " + request.getHeader(headerName) + "\n"; } msg.httpHeader = header; addMessage(msg); //logger.info("event=received_message status_code=200 msg_id=" + msg.id); doOutput(200, response, "Ok", "Ok"); } private String formatMessage(String str) { //Escape the "<" and ">" characters String fmtMsg = str.replace("<", "<"); fmtMsg = fmtMsg.replace(">", ">"); //pretty print json try { JSONObject json = new JSONObject(fmtMsg); fmtMsg = json.toString(2); } catch (Exception ex) { //not a json string } return fmtMsg; } protected void doDisplayMessages(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String pathInfo = request.getPathInfo(); String id = pathInfo.substring(pathInfo.lastIndexOf("/")+1); if (id.toLowerCase().startsWith("info")) { doOutput(404, response, "EndPoint - Display Message", "Please provide an id"); return; } List<EndpointMessage> messages = getMessages(id); if (messages == null || messages.size() == 0) { doOutput(200, response, "EndPoint - Display Messages", "No messages"); return; } String showLastStr = request.getParameter("showLast"); boolean onlyShowLast = (showLastStr != null && showLastStr.equals("true")); String output = "<table border=\"1\">"; synchronized (messages) { for (EndpointMessage msg : messages) { output += "<tr><td>Time Recv: "+msg.recvOn+"<br/>"; output += "Url: "+msg.url+"<br/>"; output += "Method: "+msg.method+"<br/>"; output += "Host: "+msg.host+"<br/>"; output += "<b>Header</b><br/>"; output += msg.httpHeader.replace("\n", "<br/>"); /*if (msg.failure) { output += "FAILED (HTTP " + failureConfigMap.get(msg.id).httpErrorCode + ")<br/>"; }*/ if (msg.msg != null && msg.msg.length() > 0) { output += "<pre>"+formatMessage(msg.msg)+"</pre><br/>"; } output += "</td></tr>"; if (onlyShowLast) { doRawOutput(200, response, msg.msg); return; } } } output += "</table>"; doOutput(200, response, "EndPoint - Display Messages", output); } protected void doOutput(int httpCode, HttpServletResponse response, String title, String body) throws IOException { response.setContentType("text/html"); response.setStatus(httpCode); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head><title>"+title+"</title></head><body>"); out.println(body); out.println("</body></html>"); } protected void doRawOutput(int httpCode, HttpServletResponse response, String msg) throws IOException { response.setContentType("text/plain"); response.setStatus(httpCode); PrintWriter out = response.getWriter(); out.print(msg); } protected void doRawOutput(int httpCode, HttpServletResponse response, char[] bytes) throws IOException { response.setContentType("text/plain"); response.setStatus(httpCode); PrintWriter out = response.getWriter(); out.print(bytes); } private byte[] generateChart(EndpointMetrics metric, String id) throws IOException { XYSeries series = new XYSeries("Test Run"); for (int i=0; i<metric.timeSeries.size(); i++) { series.add(i, metric.timeSeries.get(i)); } XYSeriesCollection dataset = new XYSeriesCollection(series); JFreeChart chart = ChartFactory.createXYBarChart( "Start: " + metric.startTime + " End: " + metric.endTime + " Message Count: " + metric.messageCount, "Test Second", false, "Number of Messages", dataset, PlotOrientation.VERTICAL, true, true, false ); //File file = new File(getServletContext().getRealPath("WEB-INF" + "/" + id + ".jpeg")); //ChartUtilities.saveChartAsJPEG(file, chart, 1600, 400); //byte b[] = Files.toByteArray(file); //return b; ByteArrayOutputStream bos = new ByteArrayOutputStream(); ChartUtilities.writeChartAsJPEG(bos, chart, 2400, 400); return bos.toByteArray(); } }