Vaadin Charts 1.1.6, SVGGenerator.generate hangs

Hi

I have been trying to update Vaadin Charts from 1.1.2 to 1.1.6, but I’m facing problems with SVGGenerator.

String svg = SVGGenerator.getInstance().generate(chart.getConfiguration()); hangs when trying to use Vaadin Charts 1.1.3 or higher. Seems that problem has something to do with changed functionality where stdin/stdout are used instead of hard coded port for exporting.

Within SVGGenerator problem occurs at method generate, exactly here:

BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = reader.readLine(); ← hangs here.

I’ve been facing exactly same problem in both Mac OS X and Ubuntu -environments.

Chart configuration that causes the problem is as following:

{
  "chart": {
    "type": "column"
  },
  "title": {
    "text": ""
  },
  "xAxis": {
    "labels": {
      "style": {
        "color": "#000000",
        "fontSize": "12px"
      }
    },
    "categories": [
      "Yksi",
      "Jump"
    ],
    "allowDecimals": false,
    "minorGridColor": "#000000",
    "tickColor": "#000000",
    "lineColor": "#000000",
    "axisIndex": 0
  },
  "yAxis": {
    "min": 0,
    "labels": {
      "style": {
        "color": "#000000",
        "fontSize": "12px"
      }
    },
    "title": {
      "text": "Vastausten määrä"
    },
    "allowDecimals": false,
    "minorGridColor": "#000000",
    "tickColor": "#000000",
    "lineColor": "#000000",
    "axisIndex": 0
  },
  "credits": {
    "enabled": false
  },
  "plotOptions": {
    "column": {
      "dataLabels": {
        "enabled": true,
        "_fn_formatter": "this.y +\u0027 (\u0027+ Math.round(this.y * 100 / 100) +\u0027%)\u0027",
        "color": "#000000",
        "connectorColor": "#000000"
      },
      "cursor": "pointer"
    }
  },
  "series": [
    {
      "data": [
        {
          "name": "1: Yksi",
          "y": 29,
          "customized": true
        },
        {
          "name": "2: Jump",
          "y": 71,
          "customized": true
        }
      ],
      "name": "Vastaukset vaihtoehtoa kohden",
      "visible": true
    }
  ],
  "exporting": {
    "enableImages": true,
    "enabled": false,
    "filename": "filename",
    "url": "",
    "width": 650
  }
}

Hi,

Update your PhantomJS version to latest version. That should do it.

cheers,
matti

I faced the same problem that the SVGGenerator hang intermittently (quite frequent) when I tried to export the Vaadin Charts to SVG image. My application and PhantomJS.exe (1.9.7) were running in Windows 7 and JRE 1.7.0_45 . Thread dump showed that the thread was locked at :

[i]
[size=2]
“http-apr-80-exec-6” - Thread t@42
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.io.BufferedInputStream.fill(Unknown Source)
at java.io.BufferedInputStream.read1(Unknown Source)
at java.io.BufferedInputStream.read(Unknown Source)

  • locked <5092aa05> (a java.io.BufferedInputStream)
    at sun.net.www.http.HttpClient.parseHTTPHeader(Unknown Source)
    at sun.net.www.http.HttpClient.parseHTTP(Unknown Source)

    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown Source)
  • locked (a sun.net.www.protocol.http.HttpURLConnection)

    at com.vaadin.addon.charts.util.SVGGenerator.generate(SVGGenerator.java:180)
  • locked <5c9a2888> (a com.vaadin.addon.charts.util.SVGGenerator)
    at com.vaadin.addon.charts.util.SVGGenerator.generate(SVGGenerator.java:162)
  • locked <5c9a2888> (a com.vaadin.addon.charts.util.SVGGenerator)
    [/size]
    [/i]

So, I applied 2 solutions by changing the SVGGenerator.java and the magic just worked! No more hanging problem after the change =)
Here are my changes:

  1. By referring to
    “Java pitfall: How to prevent Runtime.getRuntime().exec() from hanging”
    , I realised that Windows might have smaller buffer size (unlike Linux & Mac normally have buffer size = 8196) and caused the hang intermittently because there is a pitfall in Java 7 api for
    Process
    :



    “Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.”


So, I have to change it to use BufferedInputStream with smaller buffer size = 4096. Please note that I have to add this line
“sb.append(new String(bytes).replaceAll(”\x00", “”));"
because i found there could be many NULL characters being read into the buffer after it’s been iterated a few times in the while loop. If there is any NULL character in the SVG string, the PNG Transcoder will fail.[code]
// InputStream in = connection.getInputStream();
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// IOUtils.copy(in, baos);
// in.close();
// line = new String(baos.toByteArray());

String line = “”;
int BLOCK_SIZE = 4096;
try (BufferedInputStream in = new BufferedInputStream(connection.getInputStream(), BLOCK_SIZE)) {
byte bytes = new byte[BLOCK_SIZE]
;
StringBuilder sb = new StringBuilder();
while (in.read(bytes) != -1) {
sb.append(new String(bytes).replaceAll(“\x00”, “”));
bytes = new byte[BLOCK_SIZE]
;
}
line = sb.toString().trim();
[/code]

  1. Not confident the change #1 could really resolve the hanging problem completely, I also applied another killer solution: stop the PhantomJS.exe completely after the SVG string has been generated. That means the PhantomJS.exe process (with port 7878) will be killed by calling
    SVGenerator.destroy()
    each time you have done the SVG generation. Since SVGGenerator is a singleton, I have to modify its
    destroy()
    method to make sure the PhantomJS.exe is re-executed each time the SVGGenerator.getInstance() is called:

public void destroy() { if (process != null) { process.destroy(); } process = null; INSTANCE = null; } Well, after implemented change #2, the singleton SVGGenerator may face problem in a multi-thread condition (when the 1st thread may destroy the SVGGenerator instance while 2nd thread has just started to run into it), so it’s better to create a helper class with a synchronized static method to call

SVGGenerator.getInstance().generate(chart.getConfiguration());