WFF

wffweb - Java framework to develop web applications

View sample projects on GitHub
Use latest version of wffweb to get better stability, performance and thread-safety.

Unlike other java ee frameworks, wffweb doesn't have any direct dependency over java ee classes. And, websocket implementation is different in different servers and i.e. not all servers follow JSR 356 specification. So, we have to configure wffweb if its server client communication feature needs to be used. There are three main configurations. 1. Configure websocket, 2. set up a session listener and 3. set up a BrowserPage. Of course, once we have configured BrowserPage, we have to provide it by any servlet/rest.

1. Setting up websocket for wffweb

There must be a websocket url set up with your server, that websocket url should not be used by others. It should be dedicated for wffweb. The websocket should support sending and receiving binary data because wffweb and client communication is done via a binary protocol called wff binary message. when the websocket connection is opened, it must be informed to the wffweb as follows

//webSocket session
List wffInstanceIds = session.getRequestParameterMap().get("wffInstanceId");
String instanceId = wffInstanceIds.get(0);
BrowserPage browserPage = BrowserPageContext.INSTANCE.webSocketOpened(instanceId);

//or String.valueOf(session.hashcode()) if session.getId() is not available
browserPage.addWebSocketPushListener(session.getId(), new WebSocketPushListener() {

            @Override
            public void push(ByteBuffer data) {
                try {
                    session.getBasicRemote()
                            .sendBinary(data);
                } catch (Exception e) {
                    throw new PushFailedException(e.getMessage(), e);
                }
            }
        });
The above code setting a listener so that wffweb can push messages.
When the websocket is closed, it should be informed to wffweb as follows,
List wffInstanceIds = session.getRequestParameterMap().get("wffInstanceId");
String instanceId = wffInstanceIds.get(0);
//session.getId() is the id given while adding websocket listener
BrowserPageContext.INSTANCE.webSocketClosed(instanceId, session.getId());
When the websocket receives a message, it should be forwarded to wffweb as follows.
List wffInstanceIds = session.getRequestParameterMap().get("wffInstanceId");
String instanceId = wffInstanceIds.get(0);
BrowserPage browserPage = BrowserPageContext.INSTANCE.getBrowserPage(instanceId);
browserPage.webSocketMessaged(message);
Here, wffInstanceId is a unique id generated by BrowserPage instance, it can be taken by browserPage.getInstanceId method. Each instance of a BrowserPage will have its own unique instanceId .

If you want to keep the HttpSession active as long as the websocket connection is alive, you have to do the following. In OnOpen , get the httpSession object and set httpSession.setMaxInactiveInterval(-1); and in OnClose reset the session time out as httpSession.setMaxInactiveInterval(60 * 30); (the same value which is given in web.xml). An http request to a dummy url may need to be made afterwards. So if the websocket doesn't make a connection again within the given time, the httpSession will be timed out. This can also avoid keeping a heart beat request to the server to keep the httpSession alive. This is the solution for the issue explained in the description of this ticket.

Refer this fully configured code from sample project. Or checkout this demo project.

Get technical assistance

2. Setting up session listener for wffweb

When the http session is closed, it must be informed to wffweb as follows.

@WebListener
public class SessionListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent sessionEvent) {
        // NOP for wffweb
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent sessionEvent) {
        BrowserPageContext.INSTANCE
                .httpSessionClosed(sessionEvent.getSession().getId());
    }

}

BrowserPage represents UI page (window) of a browser. In a single page application, there will be only one BrowserPage.

public class IndexPage extends BrowserPage {

    @Override
    public String webSocketUrl() {
        //the websocket url you have configured for wffweb
        return "ws://yourdomain.com/wffwebdemoproject/ws-for-index-page";
    }

    @Override
    public AbstractHtml render() {

        //keep this as a separate class so as to
        //change its different portion
        Html indexPageLayout = new Html(null) {{
          new Head(this);
          
          new Body(this) {{
           
              new Div(this) {{
                
                  new H1(this) {{
                      
                    new NoTag(this, "こんにちは WFFWEB");  
                    
                  }};
                  
              }};
              
          }};
          
        }};

        return indexPageLayout;
    }

}

Whatever changes made to indexPageLayout will automatically be reflected to the client browser. So, we can keep it as a separate class for maintainability.

Once we have created a BrowserPage , we have to add it to BrowserPageContext . BrowserPageContext is the context which holds all BrowserPage instances. Adding a BrowserPage instance in to BrowserPageContext may be done inside a servlet because we need to create instance of BrowserPage only when there is a request from new session. i.e. we need to have only one instance of the IndexPage per session. It may be added as follows

@WebServlet("/index")
public class IndexPageServlet extends HttpServlet {
    
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");

        try (OutputStream os = response.getOutputStream();) {

            HttpSession session = request.getSession();

            String instanceId = (String) session
                    .getAttribute("indexPageInstanceId");

            BrowserPage browserPage = null;
            if (instanceId != null) {

                browserPage = BrowserPageContext.INSTANCE
                        .getBrowserPage(instanceId);

                // if the server is restarted browserPage could be null here,
                // so you could save this instance to db after addBrowserPage
                // method
                // and retried from db using browserPage.getInstanceId()

            }

            if (browserPage == null) {
                browserPage = new IndexPage();
                BrowserPageContext.INSTANCE.addBrowserPage(session.getId(),
                        browserPage);
                session.setAttribute("indexPageInstanceId",
                        browserPage.getInstanceId());
            }

            browserPage.toOutputStream(os, "UTF-8");
        }

    }

}

You can download a demo project from here

Next >>

For any technical assistance mail to tech-support@webfirmframework.com





Subscribe on youtube for technical videos