Wednesday 14 November 2012

PrimeFaces PUSH

On Monday this week I was going through my blogs round when I spotted this from Geertjan Wielenga,
some of you may know Geertjan as a Principal Product Manager for Oracle and NetBeans guru whose regular blog posts are well worth reading if you are a Java Dev and/or NetBeans user.

In essence what Geertjan is looking for is a way of publishing information to browsers where this information may come from hardware devices perhaps based on TinkerForge.

If you read his post you can see more about what exactly he wants to do.

I decided I would see if I could slap something together which could be used for something like this.

Requirements

  • Data to be supplied asynchronously to the web app and browsers
  • Wide browser support
  • As a Proof of Concept simulation is allowed.
My Technology Choices

I use PrimeFaces for most if not all of my JSF based WebApps and this has some very handy support for PUSH (WebSockets) which uses Atmosphere. I have never used this before so this is the perfect opportunity to try it out.

Because I wanted to do a little more than simply push the current vehicle direction and update a few p tags I also added a page which uses Raphael to provide SVG support. Basically my vehicle is a simple triangle that gets transformed into a direction supplied by data pushed to the client.

I had already suggested using a Singleton Session Bean using Timers as a way of supplying data and decided this would be the easiest way to simulate hardware being spun around and reporting its current direction to any interested clients.

I have created a Project for this on GitHub should you wish to try the code out for yourself.
Edit:

  • I have since found that Glassfish and WebSockets don't mix very well (at least with PrimeFaces).
    I created a version of the same project which runs well under Tomcat 7 where WebSocket support seems to work off the bat!
  • The AsynchDirectionChangeSimulator bean is no longer active but the same function is provided by a Servlet.

Here is the code for the Session Timer Bean that simulates the "hardware".

package org.andy.pf.tfsim.simulator;

import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import org.primefaces.push.PushContext;
import org.primefaces.push.PushContextFactory;

/**
 *
 * @author a.bailey
 */
@Singleton
@Startup
public class AsynchDirectionChangeSimulator {

    final static String[] DIRS = {
        "North",
        "Northeast",
        "East",
        "Southeast",
        "South",
        "Southwest",
        "West",
        "Northwest"
    };
    
    
    @Schedule(minute = "*", second = "*/1", hour = "*")
    public void pushDirection() {
        String nextDirection = DIRS[(int)(Math.random()*DIRS.length)];
        PushContext ctx = PushContextFactory.getDefault().getPushContext();
        if( ctx != null ) {
            ctx.push("/tfSim", nextDirection);
        }
    }
}


Please note that this looks breathlessly simple and it is because all the magic is wrapped up in the libraries being used.

Here is the page used to display my little triangle animated on push.
The code is meant to be readable by the way not perfect.


<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui">
    <f:view contentType="text/html" encoding="utf-8">
        <h:head>
            <title>TinkerForge RT Sim with SVG</title>
            <h:outputScript library="js" name="raphael-min.js"/>
            <f:facet name="last">
                <script type="text/javascript">
                    var dirTransforms = {
                       'North':"R0",
                       'Northeast':"R45",
                       'East':"R90",
                       'Southeast':"R135",
                       'South':"R180",
                       'Southwest':"R225",
                       'West':"R270",
                       'Northwest':"R315"
                    };
                    var canvas = null;
                    var tf = null;
                    $(document).ready(function() {
                       canvas = Raphael(document.getElementById("raphaelPanel"), 300, 300);
                       // tf = canvas.rect(50, 20, 40, 100);
                       tf = canvas.path("M120 180H180L150 40L120 180");
                       tf.attr("stroke", "#000");
                    });
                    function setDirection(data) {
                        tf.transform(dirTransforms[data]);
                    }
                </script>
            </f:facet>
        </h:head>
        <h:body>
            <p:panel id="raphaelPanel">
            </p:panel>
            <p:socket onMessage="setDirection" channel="/tfSim"/>
        </h:body>
    </f:view>
</html>

9 comments:

  1. Nice one Andy. Thanks for sharing. I would love to use Atmosphere with GlassFish but I will continue to try to do so, as I have had 3 to 5 failed attempts. Not the end of the world, yet. :-)

    ReplyDelete
    Replies
    1. Well I took a little time to recreate the project to get it running under Tomcat 7.
      Apart from a little hair tearing to do with Maven everything else was fine.
      Now I have to see if I can get the original project working with TomEE..

      Delete
  2. I have since tested this against TomEE and, apart from having to remove the SLF4J dependency (to prevent class version problems), it all worked, at full speed, and WebSocket support worked off the bat. Nice one Apache.

    ReplyDelete
  3. Nice one, Andy! This is great news and an excellent 'andyba' post (as always)! Honestly, I rather keep my web app running on Glassfish, so I was considering and/or wondering, if at all possible to integrate a Glassfish web app and Tomcat/websocket (PrimeFaces Push) app. Would that be done via Message beans?

    ReplyDelete
  4. There are several issues to consider here
    1- You need to be aware of possible cross scripting issues here
    2- you can embed your PF push app in anything you like as long as you are aware of 1.
    3- Message Driven Beans!

    ReplyDelete
    Replies
    1. Okay.

      1. All users access xhtml pages on GlassFish web app
      2. Those xhtml pages can reference a TomEE atmosphere channel via p:socket?
      3. Message driven beans used to push data from GlassFish web to TomEE atmosphere app?

      Delete
    2. You will need to have the TomEE driven page embedded in an iframe. The page will have to have all the components in it that are involved in the push.
      This sounds like too much trouble to be worth implementing like this. Just deploy everything from a TomEE instance instead.

      Delete
  5. Hi,
    really great, still in 2014!
    I got it to work on GlassFish 3.1.2 this way:
    - change to Primefaces 4.0 and Atmosphere 2.1.1 in the POM
    - add true in the servlet config in web.xml

    Best,
    Clement

    ReplyDelete
  6. (the last line of my comment did not get parsed correctly. I meant: &ltasync-supported&gt true &\ltasync-supported&gt

    ReplyDelete