Foreword

I will be writing a four part blog about a collaborative scenario implicating the HANA Cloud Platform and an ABAP backend Server. The blog posts will have the following content (summary):

  1. Part 1: Introduction and websocket on HCP
  2. Part 2: Moving the websocket app to ABAP
  3. Part 3: Realising cross-UI communication (dynpro and UI5)
  4. Part 4: Putting it all together: HCP – ABAP communication

Hopefully, I will manage to write one blog each week 🙂 .

I will share the source code for the application on my git: epmwebsocket repository.The source code for this week is in this commit: e3fa300

If anyone tries to deploy a copy to the cloud, use Java Web Profile 6, latest version (currently 2.63), JRE 7.

Introduction

As everything else, this protocol has its downsides when compared to plain HTTP:

  • security is “more vulnerable” (the “elevated” Version of WS, WSS is not as good as HTTPS)
  • the communication is done at a lower level
  • there are not as many proved techniques for building applications, not so many libraries, etc

So, as a personal opinion: WS should be used to issue commands to the Client and HTTP requests should be used to Transfer the actual data (e.g. via an OData Service).

WS on HCP

Our Goal is to build a UI5 application which will react dynamically when changes occur in the underlying OData Services. More specifically, I have made an app which auto-refreshes its data model whenever an user deletes an entity (we will Focus on this simple Scenario as an example, more complex cases can of course be built in a similar fashion). The data model will be SAP’s EPM model and mock data will be borrowed from the sap.m Explored apps.

Java and OData (using Olingo)

First, I have built the underlying OData Service. I used JPA for the persistency and the Apache Olingo library for implementing the Service itself. I had to make a class for each DB table (= a JPA entity) and to add these classes in a persistence unit.

epmJPA.png

For simplicity, I used the existing classes and annotations with minimal modification (i.e. no business logic was attached to the Service). The only modification from the straight-forward approach shown in this blog is that I wrapped the ODataSimpleProcessor in a delegator (to be able to hook into the delete entity calls).


public class ODataSingleProcessorWrapper extends ODataSingleProcessor {
  private ODataSingleProcessor delegate;
  public ODataSingleProcessorWrapper(ODataSingleProcessor delegate) {
       this.delegate = delegate;
  }
  //All the delegated methods follow...





I also included an Initializer class (which is actually a ServletContextListener) to fill in mock data each time the application is started. This will also be useful later on. The OData service could already be tested, by deploying the app and accessing the service’s root URL in the browser (and maybe playing around with some requests).

A simple UI5 interface

After this, I built a suitable user interface to display the data, which should at least support the Delete operation. I have used a slightly modified Fiori Master-Detail Application template for generating this user Interface.

I also needed to include the logic for handling the WS Connection. With the use of the pre-existing UI5 WS library, this was pretty simple. This was implemented in the MessageHandler class (source), where I have reused some code written a while ago, which has 2 extra features:

  • A cooldown on the refresh Operation (i.e. to not flood the Server with requests)
  • An ignore next flag (which actually prevents the app from refreshing when it triggers a delete — refresh is automatically done then, so we would in fact refresh the data twice)

For the moment, I had no WS connection URL for the application to use: this was done in the next step.

Creating the WS Endpoint

The Endpoint is a Java class which manages the Server side of the WS communication. The basic life cycle hooks of such connections are (these are method level annotations in the javax.websocket package):

  • onStart
  • onMessage
  • onClose
  • onError

For simplicity, I made a very simple Endpoint, which just keeps a synchronized collection of all sessions (connections with clients) and publishes a simple message to all of the at once. The publishing method (sendRefresh) is called when the OData Service DELETE entity method is called (that’s why the delegate OData processor was created).


@ServerEndpoint(value = "/ws")
public class Endpoint {
  private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
  public static void sendRefresh() {
       for (Session s :  sessions) {
            try {
                 s.getBasicRemote().sendText("{\"action\": \"refresh\"}");
            } catch (IOException e) { /* logging */ }
       }
  }
  @OnOpen
  public void open(Session session) {
       sessions.add(session);
  }
  @OnClose
  public void close(Session session) {
       sessions.remove(session);
  }
@OnError
  public void error(Throwable e) { /* logging */ }
}





The URL used in the ServerEndpoint annotation was also updated in the UI5 application.

Result

When the UI5 application deletes a row, the OData processor instructs the Endpoint to send a “refresh” message to all Clients, which the in turn call the refresh method on their OData models.

We can very simply test if it works: we just need to open the app in two different tabs and delete one row in one of the tabs. The application should automatically refresh the other tab’s data model (we should see the row disappear).

Untitled.jpg

That’s about for this week, next week we will see how we can port this application to run on an ABAP System. See you next week 🙂

To report this post you need to login first.

5 Comments

You must be Logged on to comment or reply to a post.

Leave a Reply