1package com.highcharts.export.util;
2
3public class SVGRasterizerException extends Exception { 4 5 private static final long serialVersionUID = -5110552374074051446L; 6 private String mistake; 7 8 public SVGRasterizerException() { 9 super();10 mistake = "unknown to men";11 }1213 public SVGRasterizerException(String err) {14 super(err); // call super class constructor15 mistake = err; // save message16 }1718 public String getError() {19 return mistake;20 }2122}
11import org.apache.batik.transcoder.image.PNGTranscoder;
12import org.apache.fop.svg.PDFTranscoder;
13
14public class SVGRasterizer {1516 private static final SVGRasterizer INSTANCE = new SVGRasterizer();1718 public static final SVGRasterizer getInstance() {19 return INSTANCE;20 }2122 private SVGRasterizer() {23 }2425 public synchronized ByteArrayOutputStream transcode(26 ByteArrayOutputStream stream, String svg, MimeType mime, Float width)27 throws SVGRasterizerException, TranscoderException {2829 TranscoderInput input = new TranscoderInput(new StringReader(svg));30 // Create the transcoder output31 TranscoderOutput transOutput = new TranscoderOutput(stream);32 // get right Transcoder, depends on mime33 SVGAbstractTranscoder transcoder = SVGRasterizer.getTranscoder(mime);34 if (width != null) {35 /*36 * If the raster image height is not provided (using the37 * KEY_HEIGHT), the transcoder will compute the raster image height38 * by keeping the aspect ratio of the SVG document.39 */40 transcoder.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH,41 width);42 }43 transcoder.transcode(input, transOutput);4445 return stream;4647 }4849 public static SVGAbstractTranscoder getTranscoder(MimeType mime)50 throws SVGRasterizerException {5152 SVGAbstractTranscoder transcoder = null;5354 switch (mime) {55 case PNG:56 transcoder = new PNGTranscoder();57 break;58 case JPEG:59 transcoder = new JPEGTranscoder();60 transcoder.addTranscodingHint(JPEGTranscoder.KEY_QUALITY,61 new Float(0.9));62 break;63 case PDF:64 transcoder = new PDFTranscoder();65 break;66 default:67 // do nothing68 break;69 }70 71 if(transcoder == null){72 throw new SVGRasterizerException("MimeType not supported");73 }74 75 return transcoder;76 }77}
23import com.highcharts.export.util.SVGRasterizer;
24import com.highcharts.export.util.SVGRasterizerException;
25
26@WebServlet(name = "Highcharts-Chart-Export", urlPatterns = { "/*" }) 27@MultipartConfig 28public class ExportController extends HttpServlet { 29 private static final long serialVersionUID = 1L; 30 private static final String REQUEST_METHOD_POST = "POST"; 31 private static final String CONTENT_TYPE_MULTIPART = "multipart/"; 32 private static final String FORBIDDEN_WORD = "<!ENTITY"; 33 protected static Logger logger = Logger.getLogger("exportservlet"); 34 35 36 public ExportController() { 37 super(); 38 } 39 40 public void init() { 41 } 42 43 protected void doGet(HttpServletRequest request, 44 HttpServletResponse response) throws ServletException, IOException { 45 processrequest(request, response); 46 } 47 48 protected void doPost(HttpServletRequest request, 49 HttpServletResponse response) throws ServletException, IOException { 50 processrequest(request, response); 51 } 52 53 public void processrequest(HttpServletRequest request, 54 HttpServletResponse response) throws IOException, ServletException { 55 56 try { 57 boolean multi = isMultipartRequest(request); 58 String svg = getParameter(request, "svg", multi); 59 if (svg == null || svg.isEmpty()) { 60 throw new ServletException( 61 "The required - svg - post parameter is missing"); 62 } 63 64 if (svg.indexOf(FORBIDDEN_WORD) > -1 || svg.indexOf(FORBIDDEN_WORD.toLowerCase()) > -1){ 65 throw new ServletException( 66 "The - svg - post parameter could contain a malicious attack"); 67 } 68 69 String filename = getFilename(getParameter(request, "filename", 70 multi)); 71 Float width = getWidth(getParameter(request, "width", multi)); 72 MimeType mime = getMime(getParameter(request, "type", multi)); 73 74 ExportController.writeFileContentToHttpResponse(svg, filename, 75 width, mime, response); 76 77 } catch (IOException ioe) { 78 logger.error("Oops something happened here redirect to error-page, " 79 + ioe.getMessage()); 80 sendError(request, response, ioe); 81 } catch ( ServletException sce) { 82 logger.error("Oops something happened here redirect to error-page, " 83 + sce.getMessage()); 84 sendError(request, response, sce); 85 } 86 87 } 88 89 /* 90 * Util methods 91 */ 92 93 public static void writeFileContentToHttpResponse(String svg, 94 String filename, Float width, MimeType mime, 95 HttpServletResponse response) throws IOException, ServletException { 96 97 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 98 99 if (!MimeType.SVG.equals(mime)) {100 try {101 stream = SVGRasterizer.getInstance().transcode(stream, svg,102 mime, width);103 } catch (SVGRasterizerException sre) {104 logger.error("Error while transcoding svg file to an image", sre);105 stream.close();106 throw new ServletException(107 "Error while transcoding svg file to an image");108 } catch (TranscoderException te){109 logger.error("Error while transcoding svg file to an image", te);110 stream.close();111 throw new ServletException(112 "Error while transcoding svg file to an image");113 }114 } else {115 stream.write(svg.getBytes());116 }117118 // prepare response119 response.reset();120 response.setContentLength(stream.size());121 response.setCharacterEncoding("utf-8");122 response.setHeader("Content-disposition", "attachment; filename="123 + filename + "." + mime.name().toLowerCase());124 response.setHeader("Content-type", mime.getType());125 // set encoding before writing to out, check this126 ServletOutputStream out = response.getOutputStream();127 // Send content to Browser128 out.write(stream.toByteArray());129 out.flush();130 }131132 public static final boolean isMultipartRequest(HttpServletRequest request) {133 // inspired by org.apache.commons.fileupload134 logger.debug("content-type " + request.getContentType());135 return REQUEST_METHOD_POST.equalsIgnoreCase(request.getMethod())136 && request.getContentType() != null137 && request.getContentType().toLowerCase()138 .startsWith(CONTENT_TYPE_MULTIPART);139 }140141 private String getParameter(HttpServletRequest request, String name,142 Boolean multi) throws IOException, ServletException {143 if (multi && request.getPart(name) != null) {144 return getValue(request.getPart(name));145 } else {146 return request.getParameter(name);147 }148 }149150 private static String getValue(Part part) throws IOException {151 BufferedReader reader = new BufferedReader(new InputStreamReader(152 part.getInputStream(), "UTF-8"));153 StringBuilder value = new StringBuilder();154 char[] buffer = new char[1024];155 for (int length = 0; (length = reader.read(buffer)) > 0;) {156 value.append(buffer, 0, length);157 }158 return value.toString();159 }160161 private String getFilename(String name) {162 return (name != null) ? name : "chart";163 }164165 private static Float getWidth(String width) {166 if (width != null && !width.isEmpty()) {167 Float parsedWidth = Float.valueOf(width);168 if (parsedWidth.compareTo(0.0F) > 0) {169 return parsedWidth;170 }171 }172 return null;173 }174175 private static MimeType getMime(String mime) {176 MimeType type = MimeType.get(mime);177 if (type != null) {178 return type;179 }180 return MimeType.PNG;181 }182183 184185 protected void sendError(HttpServletRequest request,186 HttpServletResponse response, Throwable ex) throws IOException,187 ServletException {188 String headers = null;189 String htmlHeader = "<HTML><HEAD><TITLE>Highcharts Export error</TITLE><style type=\"text/css\">"190 + "body {font-family: \"Trebuchet MS\", Arial, Helvetica, sans-serif;} table {border-collapse: collapse;}th {background-color:green;color:white;} td, th {border: 1px solid #98BF21;} </style></HEAD><BODY>";191 String htmlFooter = "</BODY></HTML>";192193 response.setContentType("text/html");194195 PrintWriter out = response.getWriter();196 Enumeration<String> e = request.getHeaderNames();197 String svg = this.getParameter(request, "svg",198 isMultipartRequest(request));199200 out.println(htmlHeader);201 out.println("<h3>Error while converting SVG</h3>");202 out.println("<h4>Error message</h4>");203 out.println("<p>" + ex.getMessage() + "</p>");204 out.println("<h4>Debug steps</h4><ol>"205 + "<li>Copy the SVG:<br/><textarea cols=100 rows=5>"206 + svg207 + "</textarea></li>"208 + "<li>Go to <a href='http://validator.w3.org/#validate_by_input' target='_blank'>validator.w3.org/#validate_by_input</a></li>"209 + "<li>Paste the SVG</li>"210 + "<li>Click More Options and select SVG 1.1 for Use Doctype</li>"211 + "<li>Click the Check button</li></ol>");212213 out.println("<h4>Request Headers</h4>");214 out.println("<TABLE>");215 out.println("<tr><th> Header </th><th> Value </th>");216217 while (e.hasMoreElements()) {218 headers = (String) e.nextElement();219 if (headers != null) {220 out.println("<tr><td align=center><b>" + headers + "</td>");221 out.println("<td align=center>" + request.getHeader(headers)222 + "</td></tr>");223 }224 }225 out.println("</TABLE><BR>");226 out.println(htmlFooter);227228 }229}
This class does not have any documentation.
Consider adding a documentation comment to explain its use.
While it may seem like the functionality of a class is perfectly obvious, any consumers of your API may not be able to pick up on certain details.
Consider a case where the class given below can be instantiated and provides certain functionalities within each instance in a thread-safe manner, perhaps it is a rest API client.
If there is no documentation comment on the class, it is not immediately obvious that the class is thread safe. Thus, multiple instances of the class may be created to perform operations concurrently, using up both memory as well as OS resources like sockets. If it were known from the beginning that the class were thread safe, the user would not need to create unnecessary extra instances of SomeClass
.
class SomeClass {
// ...
}
Make sure to add useful information regarding the usage or implementation of a particular declaration, so that anything about it which can't be understood from the name or some other cue is correctly conveyed.
/**
* Instances of this class are used to perform xyz action.
*
* This class is thread safe and the same instance can be used over multiple threads.
*/
class SomeClass {
// ...
}
This issue will not be reported for model entity classes. If there is any non-obvious behavior associated with a particular class however, do consider documenting it.