Package com.highcharts.export.controller

Source Code of com.highcharts.export.controller.ExportController

/**
* @license Highcharts JS v2.3.3 (2012-11-02)
*
* (c) 20012-2014
*
* Author: Gert Vaartjes
*
* License: www.highcharts.com/license
*/
package com.highcharts.export.controller;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.concurrent.TimeoutException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import com.highcharts.export.converter.SVGConverter;
import com.highcharts.export.converter.SVGConverterException;
import com.highcharts.export.pool.PoolException;
import com.highcharts.export.util.MimeType;
import com.highcharts.export.util.TempDir;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.servlet.http.HttpSession;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.RequestBody;

@Controller
@RequestMapping("/")
public class ExportController extends HttpServlet {
  private static final long serialVersionUID = 1L;
  private static final Float MAX_WIDTH = 2000.0F;
  private static final Float MAX_SCALE = 4.0F;
  protected static Logger logger = Logger.getLogger("exporter");

  @Autowired
  private SVGConverter converter;

  @RequestMapping(value = "/demo", method = RequestMethod.GET)
  public String demo() {
    return "demo";
  }

  @RequestMapping(method = {RequestMethod.POST, RequestMethod.GET})
  public HttpEntity<byte[]> exporter(
    @RequestParam(value = "svg", required = false) String svg,
    @RequestParam(value = "type", required = false) String type,
    @RequestParam(value = "filename", required = false) String filename,
    @RequestParam(value = "width", required = false) String width,
    @RequestParam(value = "scale", required = false) String scale,
    @RequestParam(value = "options", required = false) String options,
        @RequestParam(value = "globaloptions", required = false) String globalOptions,
    @RequestParam(value = "constr", required = false) String constructor,
    @RequestParam(value = "callback", required = false) String callback,
    @RequestParam(value = "callbackHC", required = false) String callbackHC,
    @RequestParam(value = "async", required = false, defaultValue = "false"Boolean async,
    @RequestParam(value = "jsonp", required = false, defaultValue = "false") Boolean jsonp,
    HttpServletRequest request,
    HttpSession session) throws ServletException, InterruptedException, SVGConverterException, NoSuchElementException, PoolException, TimeoutException, IOException, ZeroRequestParameterException {

    MimeType mime = getMime(type);
    String randomFilename = null;
    String jsonpCallback = "";
    boolean isAndroid = request.getHeader("user-agent") != null && request.getHeader("user-agent").contains("Android");

    if ("GET".equalsIgnoreCase(request.getMethod())) {

            // Handle redirect downloads for Android devices, these come in without request parameters
            String tempFile = (String) session.getAttribute("tempFile");
            session.removeAttribute("tempFile");

            if (tempFile != null && !tempFile.isEmpty()) {
        logger.debug("filename stored in session, read and stream from filesystem");       
        String basename = FilenameUtils.getBaseName(tempFile);
        String extension = FilenameUtils.getExtension(tempFile);

        return getFile(basename, extension);

            }
        }

        // check for visitors who don't know this domain is really only for the exporting service ;)
    if (request.getParameterMap().isEmpty()) {
       throw new ZeroRequestParameterException();
        }

    /* Most JSONP implementations use the 'callback' request parameter and this overwrites
     * the original callback parameter for chart creation with Highcharts. If JSONP is
     * used we recommend using the requestparameter callbackHC as the callback for Highcharts.
     * store the callback method name and reset the callback parameter,
     * otherwise it will be used when creation charts
     */
    if (jsonp) {
      async = true;
      jsonpCallback = callback;
      callback = null;

      if (callbackHC != null) {
        callback = callbackHC;
      }
        }
      
    if (isAndroid || MimeType.PDF.equals(mime) || async) {
      randomFilename = createRandomFileName(mime.name().toLowerCase());
    }

    /* If randomFilename is not null, then we want to save the filename in session, in case of GET is used later on*/
        if (isAndroid) {
            logger.debug("storing randomfile in session: " +  FilenameUtils.getName(randomFilename));
            session.setAttribute("tempFile", FilenameUtils.getName(randomFilename));
        }

    String output = convert(svg, mime, width, scale, options, constructor, callback, globalOptions, randomFilename);
    ByteArrayOutputStream stream;

    HttpHeaders headers = new HttpHeaders();

    if (async) {
      String link = TempDir.getDownloadLink(randomFilename);
            stream = new ByteArrayOutputStream();
            if (jsonp) {
                StringBuilder sb = new StringBuilder(jsonpCallback);
                sb.append("('");
                sb.append(link);
                sb.append("')");
                stream.write(sb.toString().getBytes("utf-8"));
                headers.add("Content-Type", "text/javascript; charset=utf-8");
            } else {
                stream.write(link.getBytes("utf-8"));
                headers.add("Content-Type", "text/html; charset=UTF-8");
            }
    } else {
      headers.add("Content-Type", mime.getType() + "; charset=utf-8");
      if (randomFilename != null && randomFilename.equals(output)) {
        stream = writeFileToStream(randomFilename);
      } else {
        boolean base64 = !mime.getExtension().equals("svg");
        stream = outputToStream(output, base64);
      }
      filename = getFilename(filename);
      headers.add("Content-Disposition",
                   "attachment; filename=" + filename.replace(" ", "_") + "." + mime.name().toLowerCase());
    }

    headers.setContentLength(stream.size());

    return new HttpEntity<byte[]>(stream.toByteArray(), headers);
  }

  @RequestMapping(value = "/files/{name}.{ext}", method = RequestMethod.GET)
  public HttpEntity<byte[]> getFile(@PathVariable("name") String name, @PathVariable("ext") String extension) throws SVGConverterException, IOException {
   
    Path path = Paths.get(TempDir.getOutputDir().toString(), name + "." + extension);
    String filename = path.toString();
    MimeType mime = getMime(extension);

    ByteArrayOutputStream stream = writeFileToStream(filename);

    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Type", mime.getType() + "; charset=utf-8");
    headers.setContentLength(stream.size());

    return new HttpEntity<byte[]>(stream.toByteArray(), headers);
  }

  @RequestMapping(value="/json/{name}.{ext}", method = RequestMethod.POST)
  public HttpEntity<byte[]> exportFromJson(
    @PathVariable("name") String name,
    @PathVariable("ext") String extension,
    @RequestBody String requestBody) throws SVGConverterException, TimeoutException, NoSuchElementException, PoolException {

    String randomFilename;
    randomFilename = null;
    String json = requestBody;
    MimeType mime = getMime(extension);

    // add outfile parameter to the json with a simple string replace
    if (MimeType.PDF.equals(mime)) {
      randomFilename = createRandomFileName(mime.name().toLowerCase());
      int ind = json.lastIndexOf("}");
      json = new StringBuilder(json).replace(ind, ind+1,",\"outfile\":\"" + randomFilename).toString() + "\"}";
    }

    String result = converter.requestServer(json);
    ByteArrayOutputStream stream;
    if (randomFilename != null && randomFilename.equals(result)) {
      stream = writeFileToStream(randomFilename);
    } else {
      boolean base64 = !mime.getExtension().equals("svg");
      stream = outputToStream(result, base64);
    }

    HttpHeaders headers = new HttpHeaders();

    headers.add("Content-Type", mime.getType() + "; charset=utf-8");
    headers.add("Content-Disposition",
                   "attachment; filename=" + name + "." + extension);
    headers.setContentLength(stream.size());

    return new HttpEntity<byte[]>(stream.toByteArray(), headers);
  }
 
  /*
   * INSTANCE METHODS
   */

  private String convert(String svg, MimeType mime, String width, String scale, String options, String constructor, String callback, String globalOptions, String filename) throws SVGConverterException, PoolException, NoSuchElementException, TimeoutException, ServletException {

    Float parsedWidth = widthToFloat(width);
    Float parsedScale = scaleToFloat(scale);
    options = sanitize(options);
    String input;
    String output;

    boolean convertSvg = false;

    if (options != null) {
      // create a svg file out of the options
      input = options;
      callback = sanitize(callback);
            globalOptions = sanitize(globalOptions);
    } else {
      // assume SVG conversion
      svg = sanitize(svg);
      if (svg == null) {
        logger.error("The mandatory 'svg' or 'options' POST parameter is undefined.");
        throw new ServletException(
            "The mandatory 'svg' or 'options' POST parameter is undefined.");
      } else {       
        convertSvg = true;
        input = svg;
      }
    }

    if (convertSvg && mime.equals(MimeType.SVG)) {
        output = converter.redirectSVG(input, filename);
    } else {
      output = converter.convert(input, mime, constructor, callback, globalOptions, parsedWidth, parsedScale, filename);
    }

    return output;
  }

  private String getFilename(String name) {
    name = sanitize(name);
    return (name != null) ? name : "chart";
  }

  private static MimeType getMime(String mime) {
    MimeType type = MimeType.get(mime);
    if (type != null) {
      return type;
    }
    return MimeType.PNG;
  }

  private static String sanitize(String parameter) {
    if (parameter == null || parameter.trim().isEmpty() || parameter.compareToIgnoreCase("undefined") == 0 || parameter.compareTo("null") == 0 || parameter.compareTo("{}") == 0) {
      return null;
    }
    return parameter.trim();
  }

  private static Float widthToFloat(String width) throws SVGConverterException {
    width = sanitize(width);
    if (width != null) {
      width = width.replace("px", "");
      try {
        Float parsedWidth = Float.valueOf(width);
        if (parsedWidth.compareTo(MAX_WIDTH) > 0) {
          return MAX_WIDTH;
        }
        if (parsedWidth.compareTo(0.0F) > 0) {
          return parsedWidth;
        }
      } catch (NumberFormatException nfe) {
        logger.error("Parameter width is wrong for value: " + width, nfe.fillInStackTrace());
        throw new SVGConverterException("Parameter width is wrong for value: " + width);
      }
     
    }
    return null;
  }

  private static Float scaleToFloat(String scale) throws SVGConverterException {
    scale = sanitize(scale);
    if (scale != null) {
      try {
        Float parsedScale = Float.valueOf(scale);
        if (parsedScale.compareTo(MAX_SCALE) > 0) {
          return MAX_SCALE;
        } else if (parsedScale.compareTo(0.0F) > 0) {
          return parsedScale;
        }
      } catch (NumberFormatException nfe) {
        logger.error("Parameter scale is wrong for value: " + scale, nfe.fillInStackTrace());
        throw new SVGConverterException("Parameter scale is wrong for value: " + scale);
      }
    }
    return null;
  }

  public String createRandomFileName(String extension) {
    Path path = Paths.get(TempDir.outputDir.toString(), RandomStringUtils.randomAlphanumeric(8) + "." + extension);
    return path.toString();
  }

  private ByteArrayOutputStream outputToStream(String output, boolean base64) throws SVGConverterException {
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    try {
      if (base64) {
         //decode the base64 string
        stream.write(Base64.decodeBase64(output));
      } else {
        stream.write(output.getBytes());
      }
    } catch (IOException ex) {
      logger.error("Error in outputToStream: " + ex.getMessage());
      throw new SVGConverterException("Error while converting");
    }

    return stream;
  }

  private ByteArrayOutputStream writeFileToStream(String filename) throws SVGConverterException {
    ByteArrayOutputStream stream = new ByteArrayOutputStream();

    try {
      stream.write(FileUtils.readFileToByteArray(new File(filename)));
    } catch (IOException ioex) {
      logger.error("Tried to read file from filesystem: " + ioex.getMessage());
      throw new SVGConverterException("IOException: cannot find your file to download...");
    }

    return stream;
  }

  /* EXCEPTION HANDLERS */

  @ExceptionHandler(ZeroRequestParameterException.class)
  public ModelAndView handleZeroRequestParameterException(Exception ex, HttpServletResponse response) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("demo");
    response.setStatus(302);
    return modelAndView;
  }

  @ExceptionHandler(IOException.class)
  public ModelAndView handleIOException(Exception ex, HttpServletResponse response) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("error");
    modelAndView.addObject("message", ex.getMessage());
    response.setStatus(500);
    return modelAndView;
  }

  @ExceptionHandler(TimeoutException.class)
  public ModelAndView handleTimeoutException(Exception ex, HttpServletResponse response) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("error");
    modelAndView.addObject("message",
        "Timeout converting SVG, is your file this big, or maybe you have a syntax error in the javascript callback?");
    response.setStatus(500);
    return modelAndView;
  }

  @ExceptionHandler(PoolException.class)
  public ModelAndView handleServerPoolException(Exception ex, HttpServletResponse response) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("error");
    modelAndView.addObject("message",
        "Sorry, the server is handling too many requests at the moment. Please try again.");
    response.setStatus(500);
    return modelAndView;
  }

  @ExceptionHandler(SVGConverterException.class)
  public ModelAndView handleSVGRasterizeException(Exception ex, HttpServletResponse response) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("error");
    modelAndView.addObject("message",
        "Something went wrong while converting. " + ex.getMessage());
    response.setStatus(500);
    return modelAndView;
  }

  @ExceptionHandler(InterruptedException.class)
  public ModelAndView handleInterruptedException(Exception ex, HttpServletResponse response) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.setViewName("error");
    modelAndView.addObject("message",
        "It took too long time to process the options, no SVG is created. Make sure your javascript is correct");
    response.setStatus(500);
    return modelAndView;
  }

  @ExceptionHandler(ServletException.class)
  public ModelAndView handleServletException(Exception ex, HttpServletResponse response) {
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("message", ex.getMessage());
    response.setStatus(500);
    return modelAndView;
  }
}
TOP

Related Classes of com.highcharts.export.controller.ExportController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.