Our Live-Ticker implementation – World cup 2014

Post to Twitter

We got a lot of (positive) feedback for our live ticker in Packung! It’s great to see actual results depending on the current score :)

We started our live ticker in 2008 with a simple implementation in Java. The solution simply sniffed results from standard html pages. It was easy to get results because result pages didn’t use Ajax. It was harder in 2010 and 2012 because Ajax was heavily used. But we found some pages without Ajax.

What’s the problem with Ajax? It’s not possible to get the complete html content with one simple http request with Java. You need a “browser” that handles javascript and async calls.

We didn’t find a html page with live results this year, which didn’t use Ajax or Flash. So we thought about a clever solution because we won’t pay for “free content”. And of course, our betting game is free and we don’t earn money. But what’s a clever solution? The task is simple: Grab results from existing live-ticker website (be aware of Ajax) – but how?

The solution is simple: Use a browser :)

But Java doesn’t offer a browser control, doesn’t it? Yes, JavaFX does!

In 2008 we played around with QT webkit control of QT Jambi (but the project is not active) because it was a nice browser control. The problem was that it didn’t work headless and our application server was a linux box without X-Server. This year, we had the same restrictions because our server is a linux box and still no X-Server. We thought that JavaFX could be the right solution for us…

BUT it doesn’t work headless enough – not with latest release. It will be better in upcoming releases and/or custom JavaFX builds because there is a Glass windowing component called Monocle. But this was the right challenge for us.

Our current solution runs on Linux, without a real X-Server. It’s a JavaFX WebView/WebEngine that reads data from a public live-ticker.

How our solution works?

Install important linux packages:

yum install xorg-x11-server-Xvfb xorg-x11-xauth gcc glib2 glib2-devel libtiff libtiff-devel libjpeg-devel cairo cairo-devel pango pango-devel redhat-lsb

optional if available

yum install redhat-lsb-graphics

Download and install ATK

wget ftp://ftp.muug.mb.ca/mirror/centos/6/os/x86_64/Packages/atk-1.30.0-1.el6.x86_64.rpm
wget ftp://ftp.muug.mb.ca/mirror/centos/6/os/x86_64/Packages/atk-devel-1.30.0-1.el6.x86_64.rpm
rpm -Uvh *.rpm

and GTK+

wget http://ftp.gnome.org/pub/gnome/sources/gtk+/2.18/gtk+-2.18.2.tar.gz
tar -xvf gtk+-2.18.2.tar.gz
cd gtk+-2.18.2
./configure
make
make install

Start Xvfb:

Xvfb :99 &

Start application:

#!/bin/sh
export DISPLAY=:99
export LD_LIBRARY_PATH=/usr/local/lib
./jdk1.8.0_05/bin/java -jar livescore.jar >> liveupdate.log 2>> liveupdate_err.log &

The application itself is trivial. It’s a class with main method:

/**
 * Starts the application.
 *
 * @param pArgs the application parameters
 */

public static void main(String[] pArgs)
{
    new Livescore();
}

/**
 * Creates a new instance of <code>Livescore</code> and starts the update procedure.
 */

public Livescore()
{
    super();

    fxPanel = new JFXPanel();
       
    Platform.runLater(new Runnable()
    {
        public void run()
        {
            fxPanel.setScene(new Scene(new BorderPane(), 800, 600));
               
            browser = new SimpleBrowser();
            browser.load();
        }
    });
       
    start();
}

We need the JFXPanel because JavaFX needs a scene, otherwise it’ll throw Exceptions. We don’t need the instance for other things.

The SimpleBrowser class is similar to this snippet:

/**
 * Loads the configured website.
 */

public void load()
{
    if (view == null)
    {
        view = new WebView();
        engine = view.getEngine();

        engine.load("http://live-ticker.url");
    }
    else
    {
        engine.reload();
    }

    if (thCheck == null)
    {
        thCheck = new Thread(new Runnable()
        {
            public void run()
            {
                try
                {
                    while (Thread.currentThread() == thCheck)
                    {
                        Platform.runLater(new Runnable()
                        {
                            public void run()
                            {
                                checkResults();
                            }
                        });
                       
                        Thread.sleep(10000);
                    }
                }
                catch (InterruptedException ie)
                {
                    //stop
                }
               
                thCheck = null;
            }
        });
        thCheck.start();
    }
}

That’s it. We got two “silent” Exceptions

java.lang.NullPointerException
  at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:57)
  at sun.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)
  at java.lang.reflect.Field.get(Field.java:387)
  at com.sun.javafx.webkit.theme.ScrollBarThemeImpl.getFieldValue(ScrollBarThemeImpl.java:397)
 …
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
  at com.sun.javafx.webkit.theme.ScrollBarThemeImpl.thumbPosition(ScrollBarThemeImpl.java:267)
  at com.sun.javafx.webkit.theme.ScrollBarThemeImpl.getThumbPosition(ScrollBarThemeImpl.java:352)
  at com.sun.webkit.Timer.twkFireTimerEvent(Native Method)
  at com.sun.webkit.Timer.fireTimerEvent(Timer.java:66)
  at com.sun.webkit.Timer.notifyTick(Timer.java:47)
  at javafx.scene.web.WebEngine$PulseTimer$2.pulse(WebEngine.java:1154)
  …

after the application start, because of some scrolling problems, but these Exceptions had no effect (in our solution).

The solution would rock with a real headless JavaFX but it’s ok for us.

2 Responses to “Our Live-Ticker implementation – World cup 2014”

  1. Agust says:

    To remove the “silent” exception you might try to call engine.requestLayout() before the load part is called (using a separate Platform.runLater … – worked for me in a similar situation.

  2. rjahn says:

    Engine doesn’t offer requestLayout, but WebView does. It’s a javafx.scene.Parent method.

    I tried to call requestLayout but it didn’t fix the exception. load was called via runLater…

    The problem is reproducable with Windows… it has nothing to do with “headless mode”.

Leave a Reply

Spam protection by WP Captcha-Free