/******************************************************************************* * * Copyright (c) 2004-2011, Oracle Corporation * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Kohsuke Kawaguchi, Winston.Prakash@Oracle.com * *******************************************************************************/ package org.eclipse.hudson.graph; import hudson.model.Descriptor.FormException; import java.awt.Font; import java.awt.Graphics2D; import java.awt.HeadlessException; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import javax.servlet.ServletOutputStream; import javax.imageio.ImageIO; import java.io.IOException; import java.util.Calendar; import java.awt.image.BufferedImage; import java.util.List; /** * A Graph interface for graphing support. Actual graph generation is delegated * to GraphSupport object. Plugin can provide their own graphing support via * GraphSupport Extension Point. * * <p> This object exposes two URLs: <dl> <dt>/png <dd>PNG image of a graph * * <dt>/map <dd>Clickable map </dl> * * @since 2.3.0 */ public class Graph { public static final int TYPE_STACKED_AREA = 1; public static final int TYPE_LINE = 2; public static final int TYPE_STACKED_BAR = 3; private final long timestamp; private int width; private int height; private volatile GraphSupport graphSupport; /** * @param timestamp Timestamp of this graph. Used for HTTP cache related * headers. If the graph doesn't have any timestamp to tie it to, pass -1. */ public Graph(long timestamp, int defaultW, int defaultH) { this.timestamp = timestamp; this.width = defaultW; this.height = defaultH; if (!GraphSupport.all().isEmpty()) { try { graphSupport = GraphSupport.all().get(0).newInstance(null, null); } catch (FormException ex) { ex.printStackTrace(); } } } public Graph(Calendar timestamp, int defaultW, int defaultH) { this(timestamp.getTimeInMillis(), defaultW, defaultH); } public synchronized GraphSupport getGraphSupport() { return graphSupport; } public void setGraphSupport(GraphSupport graphSupport) { this.graphSupport = graphSupport; } public void setChartType(int chartType) { if (graphSupport != null) { graphSupport.setChartType(chartType); } } public void setMultiStageTimeSeries(List<MultiStageTimeSeries> multiStageTimeSeries) { if (graphSupport != null) { graphSupport.setMultiStageTimeSeries(multiStageTimeSeries); } } public void setData(DataSet data) { if (graphSupport != null) { graphSupport.setData(data); } } public void setTitle(String title) { if (graphSupport != null) { graphSupport.setTitle(title); } } public void setXAxisLabel(String xLabel) { if (graphSupport != null) { graphSupport.setXAxisLabel(xLabel); } } public void setYAxisLabel(String yLabel) { if (graphSupport != null) { graphSupport.setYAxisLabel(yLabel); } } public BufferedImage createImage(int width, int height) { BufferedImage image = null; if (graphSupport != null) { image = graphSupport.render(width, height); } else { image = createErrorImage(width, height); } return image; } /** * Renders the graph. */ public void doPng(StaplerRequest req, StaplerResponse rsp) throws IOException { if (req.checkIfModified(timestamp, rsp)) { return; } try { String w = req.getParameter("width"); if (w != null) { width = Integer.parseInt(w); } String h = req.getParameter("height"); if (h != null) { height = Integer.parseInt(h); } rsp.setContentType("image/png"); ServletOutputStream os = rsp.getOutputStream(); BufferedImage image = createImage(width, height); ImageIO.write(image, "PNG", os); os.close(); } catch (Error e) { /* OpenJDK on ARM produces an error like this in case of headless error Caused by: java.lang.Error: Probable fatal error:No fonts found. at sun.font.FontManager.getDefaultPhysicalFont(FontManager.java:1088) at sun.font.FontManager.initialiseDeferredFont(FontManager.java:967) .. */ if (e.getMessage().contains("Probable fatal error:No fonts found")) { rsp.sendRedirect2(req.getContextPath() + "/images/headless.png"); return; } throw e; // otherwise let the caller deal with it } catch (HeadlessException e) { // not available. send out error message rsp.sendRedirect2(req.getContextPath() + "/images/headless.png"); } } /** * Create a Image map with the given name, width and height * * @param mapName * @param width * @param height * @return */ public String createImageMap(String mapName, int width, int height) { return graphSupport.getImageMap(mapName, width, height); } /** * Send the a clickable map data information. */ public void doMap(StaplerRequest req, StaplerResponse rsp) throws IOException { if (graphSupport != null) { if (req.checkIfModified(timestamp, rsp)) { return; } String w = req.getParameter("width"); if (w != null) { width = Integer.parseInt(w); } String h = req.getParameter("height"); if (h != null) { height = Integer.parseInt(h); } rsp.setContentType("text/plain;charset=UTF-8"); String mapHtml = createImageMap("map", width, height); rsp.getWriter().println(mapHtml); } } private BufferedImage createErrorImage(int width, int height) { BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D graphics = img.createGraphics(); graphics.setColor(ColorPalette.RED); Font font = new Font("Serif", Font.BOLD, 14); graphics.drawRect(2, 2, width - 4, height - 4); graphics.drawString("Graph Support missing. \n Install Graph Support Plugin", 10, height / 2); return img; } }