Recently we have integrated Moxie Group’s GWT Highchart in our project and ran into the conflict with ExportController’s servlet-api-3.0 multi-part issues.
Like the old GWT does, it uses and includes servlet-api-2.5 or rather jetty 7. Fortunately our project has already got apache-fileupload in its dependency and we could use the fileitemiterator to read the multi-part items. Changes are made in processrequest(HttpServletRequest request, HttpServletResponse response) and getParameter(HttpServletRequest request, String name) Here comes the modified code:
package com.highcharts.export.controller; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Enumeration; 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.apache.batik.transcoder.TranscoderException; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.FileItemIterator; import org.apache.commons.fileupload.FileItemStream; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.log4j.Logger; import org.mortbay.log.Log; import com.highcharts.export.util.MimeType; import com.highcharts.export.util.SVGRasterizer; import com.highcharts.export.util.SVGRasterizerException; public class ExportController extends HttpServlet { private static final long serialVersionUID = 1L; private static final String REQUEST_METHOD_POST = "POST"; private static final String CONTENT_TYPE_MULTIPART = "multipart/"; private static final String FORBIDDEN_WORD = "<!ENTITY"; protected static Logger logger = Logger.getLogger("exportservlet"); public ExportController() { super(); } public void init() { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processrequest(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processrequest(request, response); } public void processrequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String svg = getParameter(request, "svg"); String filename = getFilename(getParameter(request, "filename")); Float width = getWidth(getParameter(request, "width")); MimeType mime = getMime(getParameter(request, "type")); try { boolean multi = isMultipartRequest(request); if (multi) { try { final FileItemFactory factory = new DiskFileItemFactory(); final ServletFileUpload upload = new ServletFileUpload(factory); final FileItemIterator filesIterator = upload.getItemIterator(request); while (filesIterator.hasNext()) { final FileItemStream item = filesIterator.next(); final String name = item.getFieldName(); if ("svg".equals(name)) { svg = new String(toByteArray(item), "UTF-8"); } else if ("filename".equals(name)) { filename = getFilename(new String(toByteArray(item), "UTF-8")); } else if ("width".equals(name)) { width = getWidth(new String(toByteArray(item), "UTF-8")); } else if ("type".equals(name)) { mime = getMime(new String(toByteArray(item), "UTF-8")); } } } catch (Exception e) { Log.warn("Failed to parse multi-part parameter: " + e); } } else { } if (svg == null || svg.isEmpty()) { throw new ServletException("The required - svg - post parameter is missing"); } if (svg.indexOf(FORBIDDEN_WORD) > -1 || svg.indexOf(FORBIDDEN_WORD.toLowerCase()) > -1) { throw new ServletException("The - svg - post parameter could contain a malicious attack"); } ExportController.writeFileContentToHttpResponse(svg, filename, width, mime, response); } catch (IOException ioe) { logger.error("Oops something happened here redirect to error-page, " + ioe.getMessage()); sendError(request, response, ioe); } catch (ServletException sce) { logger.error("Oops something happened here redirect to error-page, " + sce.getMessage()); sendError(request, response, sce); } } public static byte[] toByteArray(final FileItemStream item) throws IOException { final InputStream in = item.openStream(); final ByteArrayOutputStream out = new ByteArrayOutputStream(); copy(in, out); final byte[] byteArray = out.toByteArray(); in.close(); return byteArray; } public static void copy(final InputStream in, final OutputStream out) throws IOException { final byte[] buf = new byte[1024 * 8]; while (true) { final int length = in.read(buf); if (length == -1) { break; } out.write(buf, 0, length); } out.flush(); } /* * Util methods */ public static void writeFileContentToHttpResponse(String svg, String filename, Float width, MimeType mime, HttpServletResponse response) throws IOException, ServletException { ByteArrayOutputStream stream = new ByteArrayOutputStream(); if (!MimeType.SVG.equals(mime)) { try { stream = SVGRasterizer.getInstance().transcode(stream, svg, mime, width); } catch (SVGRasterizerException sre) { logger.error("Error while transcoding svg file to an image", sre); stream.close(); throw new ServletException("Error while transcoding svg file to an image"); } catch (TranscoderException te) { logger.error("Error while transcoding svg file to an image", te); stream.close(); throw new ServletException("Error while transcoding svg file to an image"); } } else { stream.write(svg.getBytes()); } // prepare response response.reset(); response.setContentLength(stream.size()); response.setCharacterEncoding("utf-8"); response.setHeader("Content-disposition", "attachment; filename=" + filename + "." + mime.name().toLowerCase()); response.setHeader("Content-type", mime.getType()); // set encoding before writing to out, check this ServletOutputStream out = response.getOutputStream(); // Send content to Browser out.write(stream.toByteArray()); out.flush(); } public static final boolean isMultipartRequest(HttpServletRequest request) { // inspired by org.apache.commons.fileupload logger.debug("content-type " + request.getContentType()); return REQUEST_METHOD_POST.equalsIgnoreCase(request.getMethod()) && request.getContentType() != null && request.getContentType().toLowerCase().startsWith(CONTENT_TYPE_MULTIPART); } private String getParameter(HttpServletRequest request, String name) throws IOException, ServletException { return request.getParameter(name); } private String getFilename(String name) { return (name != null) ? name : "chart"; } private static Float getWidth(String width) { if (width != null && !width.isEmpty()) { Float parsedWidth = Float.valueOf(width); if (parsedWidth.compareTo(0.0F) > 0) { return parsedWidth; } } return null; } private static MimeType getMime(String mime) { MimeType type = MimeType.get(mime); if (type != null) { return type; } return MimeType.PNG; } protected void sendError(HttpServletRequest request, HttpServletResponse response, Throwable ex) throws IOException, ServletException { String headers = null; String htmlHeader = "<HTML><HEAD><TITLE>Highcharts Export error</TITLE><style type=\"text/css\">" + "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>"; String htmlFooter = "</BODY></HTML>"; response.setContentType("text/html"); PrintWriter out = response.getWriter(); Enumeration<String> e = request.getHeaderNames(); String svg = this.getParameter(request, "svg"); out.println(htmlHeader); out.println("<h3>Error while converting SVG</h3>"); out.println("<h4>Error message</h4>"); out.println("<p>" + ex.getMessage() + "</p>"); out.println("<h4>Debug steps</h4><ol>" + "<li>Copy the SVG:<br/><textarea cols=100 rows=5>" + svg + "</textarea></li>" + "<li>Go to <a href='http://validator.w3.org/#validate_by_input' target='_blank'>validator.w3.org/#validate_by_input</a></li>" + "<li>Paste the SVG</li>" + "<li>Click More Options and select SVG 1.1 for Use Doctype</li>" + "<li>Click the Check button</li></ol>"); out.println("<h4>Request Headers</h4>"); out.println("<TABLE>"); out.println("<tr><th> Header </th><th> Value </th>"); while (e.hasMoreElements()) { headers = (String) e.nextElement(); if (headers != null) { out.println("<tr><td align=center><b>" + headers + "</td>"); out.println("<td align=center>" + request.getHeader(headers) + "</td></tr>"); } } out.println("</TABLE><BR>"); out.println(htmlFooter); } } <pre>