Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
2,076

This article describes how to run WebSockets on sap AS Java 7.4.

First of all, couple of words, why do we need WebSockets at all.

0. Brief introduction.

If you need real time client-server communication (e.g. chat, collaborative text editor etc) for today you

have several capabilities:

1. Comet (long-held HTTP request allows a web server to push data to a browser, without the browser explicitly requesting it) https://en.wikipedia.org/wiki/Comet_(programming)

Main disadvantage of such an approach - AS needs to maintain vast amounts of open http connections. And more abstractly - we are using

http connections in a radically different way compared they should be used.

2. Ajax polling. (client requests information from server periodically). Disadvantages: hight load without "real" real time eventing.

3. WebSocket (protocol providing full-duplex communication channels over a single TCP connection) https://en.wikipedia.org/wiki/WebSocket

WebSocket is part of JSR 356 and part of JEE7 specification, and as far as I googled, SAP supports WebSockets for abap stack

or in hana beta version. And SAP AS Java 7.40 runs on JEE5.

So we need to "teach" AS to understand WebSocket protocol. In this article I will describe how to do this with Jetty. https://en.wikipedia.org/wiki/Jetty_(web_server)

Other intresting approaches: JWebSocket,node.js,tomcat 7

1. How to install Embedded Jetty into SAP AS Java.

We don't have Servlet 3.0 in Java AS 7.4, so we will have to download 7 jetty versio, this jars from repository:

  • jetty-continuation-7.6.19.v20160209.jar,
  • jetty-http-7.6.19.v20160209.jar,
  • jetty-io-7.6.19.v20160209.jar,
  • jetty-server-7.6.19.v20160209.jar,
  • jetty-util-7.6.19.v20160209.jar,
  • jetty-websocket-7.6.19.v20160209.jar

For latter jetty versions you will have to backport Servlet 3.0 to portal 7.4 and this task doesn't look like simple :smile:

You can use this link to find thise jars http://www.eclipse.org/jetty/download.html

     2. Write WebSocketHandler.

You need to extend WebSocketHandler abstract class and override some of it's methods (doWebSocketConnect,WebSocket.OnTextMessage,

onOpen, onMessage, onClose etc). Here is example code:


package ;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketHandler;
import com.sap.tc.logging.Location;
public class ChatWebSocketHandler extends WebSocketHandler {
  private Location logger = Location.getLocation(ChatWebSocketHandler.class);
  /**
  * Active web sockets set
  */
  private final Set<ChatWebSocket> webSockets = new CopyOnWriteArraySet<ChatWebSocket>();
  /**
  * Executing on new connetcion establishment
  *
  * @param request
  * @param protocol
  *            protocol (it can be ws and wss)
  * @return
  */
  @Override
  public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) {
  logger.debugT("ChatWebSocketHandler doWebSocketConnect protocol: "+protocol);
  // We can refuse client and throw exception
  // throw new Exception();
  // or we can return an object that will contain an abstraction
  // of client-server connetcion and will handle
  // client requests
  return new ChatWebSocket();
  }
  private class ChatWebSocket implements WebSocket.OnTextMessage {
  private Connection connection;//here we store commection
  private String userName = null;//user nickname
  private final Pattern authCmdPattern = Pattern.compile("^\\/auth ([\\S]+).*");//income auth command pattern
  /**
  * Get users request command pattern
  */
  private final Pattern getUsersCmdPattern = Pattern.compile("^\\/getUsers.*");
  /**
  * help request command pattern
  */
  private final Pattern helpCmdPattern = Pattern.compile("^\\/help.*");
  /**
  * Executed on new connection opening
  *
  * @param connection
  */
  @Override
  public void onOpen(Connection connection) {
  logger.debugT("ChatWebSocketHandler onOpen ");
  // Saving connection into ChatWebSocket::connection
  this.connection = connection;
  // Adding this handler to global set
  // ChatWebSocketHandler::webSockets
  webSockets.add(this);
  }
  /**
  * executing on ne message from client
  *
  * @param data
  */
  @Override
  public void onMessage(String data) {
  logger.debugT("ChatWebSocketHandler onMessage ");
  // If we have authorization command
  if (authCmdPattern.matcher(data).matches()) {
  Matcher matcher = authCmdPattern.matcher(data);
  matcher.find();
  // Setting new user nickname
  userName = matcher.group(1);
  try {
  // Iterating over sockets
  // ChatWebSocketHandler::webSockets
  for (ChatWebSocket webSocket : webSockets) {
  // sending message about new user connected
  webSocket.connection.sendMessage("inf|"
  + (webSocket.equals(this) ? "You have entered" : ("User entered chat <b>" + userName + "</b>")));
  }
  } catch (IOException x) {
  // All errors will lead to disconnect
  connection.disconnect();
  }
  // If we have get users command
  } else if (getUsersCmdPattern.matcher(data).matches()) {
  String userList = "";
  // Iterating over sockets
  for (ChatWebSocket webSocket : webSockets) {
  userList += webSocket.userName + ", ";
  }
  userList = userList.substring(0, userList.length() - 2);
  try {
  //Sending active users list
  connection.sendMessage("inf|User list: " + userList);
  } catch (IOException x) {
  // All errors will lead to disconnect
  connection.disconnect();
  }
  // If we have /help command
  } else if (helpCmdPattern.matcher(data).matches()) {
  String helpMessage = "You can send messages by just putting them " + into the field and pressing Enter.<br />"
  + "Chat can handle commands:<br />" + "<ul><li><b>/help</b> - to print this message</li>"
  + "<li><b>/getUsers</b> - to get active users list</li>" + "<li><b>/auth <i>Nickname</i></b> - for authorization</li></ul>";
  try {
  // Sending instruction
  connection.sendMessage("inf|" + helpMessage);
  } catch (IOException x) {
  // All errors will lead to disconnect
  connection.disconnect();
  }
  // If we have message(not a command)
  } else {
  try {
  // If current user did't authorize
  if (userName == null) {
  connection.sendMessage("err|U did't log on<br />" + "Use command <b>/help</b> for help");
  return;
  }
  // Iterating over sockets
  // ChatWebSocketHandler::webSockets
  for (ChatWebSocket webSocket : webSockets) {
  // sending in message for everyone exept author
  // for author - out flag
  webSocket.connection.sendMessage((webSocket.equals(this) ? "out|" : ("in|" + userName + "|")) + data);
  }
  } catch (IOException x) {
  // All errors will lead to disconnect
  connection.disconnect();
  }
  }
  }
  /**
  * Executing when user disconnects from server
  *
  * @param closeCode
  * @param message
  */
  @Override
  public void onClose(int closeCode, String message) {
  logger.debugT("ChatWebSocketHandler onClose  closeCode: "+closeCode+" message: "+message);
  // Removing ourselves from global set
  // ChatWebSocketHandler::webSockets
  webSockets.remove(this);
  }
  }
}




3. Write WebSocket Server.

I used simple HttpServlet to start server (placed startServer method in its init method), but you can start server from any kind of application, e.g. WD4J or just a jar with main method etc.



private void startServer() {
  logger.debugT("Starting Jetty");
  try {
  // Creating Jetty Server on 8081 port
  this.server = new Server(8081);
  logger.debugT("Creating Jetty Server on 8081 port");
  // Registerring ChatWebSocketHandler in Jetty server
  ChatWebSocketHandler chatWebSocketHandler = new ChatWebSocketHandler();
  logger.debugT("Registerring ChatWebSocketHandler in Jetty server");
  chatWebSocketHandler.setHandler(new DefaultHandler());
  server.setHandler(chatWebSocketHandler);
  // Starting jetty
  server.start();
  logger.debugT("Jetty started");
  } catch (Throwable e) {
  logger.errorT("startServer error!");
  e.printStackTrace();
  }
  }




4. Write some client code.

You can write a simple html with javascript code:


var websocket;
window.onload = function() {
//web sockets starts here
       websocket = new WebSocket('ws://host:8081');
       websocket.onopen = function(evt) { onOpen(evt) };
       websocket.onclose = function(evt) { onClose(evt) };
       websocket.onmessage = function(evt) { onMessage(evt) };
       websocket.onerror = function(evt) { onError(evt) };
}




Or you can use for test https://www.websocket.org/echo.html

2 Comments
Labels in this area