Skip to Content

This is about a new way of communicating between different layers in ABAP. It is about bidirectional event driven communication either between different application servers of an AS ABAP or between application servers of an AS ABAP and the Internet. Up to now  communication between these enitities was restricted to caller-receiver scenarios (RFC, ICF). Eventing (publish and subscribe) was possible only inside an internal session with the events of ABAP Objects. The new ABAP channels allow to send and receive event messages in AS ABAP to other application servers or the Internet. This will be helpful for all collaborative scenarios and might revolutionize the way of how such entities are working together (imagine an application server independend ABAP based monitor, e.g. SM50, where you don’t have to push the refresh button yourself but the list refreshes itself!).

Admittetly, I’m not too deep in that subject but even I have been able to create and run some basic demo programs that I will use to give you a short introduction.

But first a teaser in form of a gaming example.

Gaming Example

/wp-content/uploads/2013/07/ping_pong_252457.jpg

A ping pong game implented with ABAP Channels!. The field is shown in a browser. Players can be SAP GUIs or browsers (also on smart phones ) in any combination. It will be demonstrated and can be tried in workshop CD261 at this years TechEd 😀 .

This as an eye catcher that shows how collaborative ABAP can be. The code itself is for sure a bit less instructive than my own (boring but simple) examples that will show you what lies behind. What you can do is to take the following snippets as startig points and create your own games or something really useful.

ABAP Messaging Channels (AMC)

ABAP Messaging Channels (AMC) allow an event based communication between ABAP programs of different application servers of an AS ABAP using messages.

An AMC is a repository object maintained in SE80 (no, not in ADT up to now …) or directly using transaction SAMC where you specify

  • the application name that is a kind of name space for the channels
  • the type of the message of the channel (text or binary)
  • the scope of the channel (who can receive messages of that channel: system, client, user)
  • authorized programs (whitelist of programs which are allowed to send or receive messages of that channel or to bind the AMC to an APC)

For sending and receiving AMC messages, you use classes and interfaces CL_AMC_… or. IF_AMC_… of an API.

Example for sender

TRY.
    CAST if_amc_message_producer_text(
          cl_amc_channel_manager=>create_message_producer(
            i_application_id = ‘DEMO_AMC’
            i_channel_id    = ‘/demo_text’ )
      )->send( i_message = |Message from { sy-repid }| ).
  CATCH cx_amc_error INTO DATA(text_exc).
    cl_demo_output=>display( text_exc->get_text( ) ).
ENDTRY.

CALL TRANSFORMATION id SOURCE message = `Hello AMC!`
                      RESULT XML DATA(xml).
TRY.
    CAST if_amc_message_producer_binary(
          cl_amc_channel_manager=>create_message_producer(
            i_application_id = ‘DEMO_AMC’
            i_channel_id    = ‘/demo_binary’ )
      )->send( i_message = xml ).
  CATCH cx_amc_error INTO DATA(binary_exc).
    cl_demo_output=>display( binary_exc->get_text( ) ).
ENDTRY.

The messaging channels used are defiined as demo_text and /demo_binary in the repository, where they are organized in an application named DEMO_AMC. To send messages using these channels you need sender objects that you can create with the factory method CREATE_MESSAGE_PRODUCER of the system class CL_AMC_CHANNEL_MANAGER. The sending of character, byte strings or PCP (a kind of name/value plus a body , very similar to  HTTP message) is done with the method SEND of different interfaces for text and binary for which a casting is performed.

You can run this code in 7.40 and … you will see nothing until you provide a consumer for the messages sent,

Example for consumer

CLASS message_receiver DEFINITION.
  PUBLIC SECTION.
    INTERFACES: if_amc_message_receiver_text,
                if_amc_message_receiver_binary.
    DATA: text_message TYPE string,
          binary_message TYPE xstring.
ENDCLASS.

CLASS message_receiver IMPLEMENTATION.
  METHOD if_amc_message_receiver_text~receive.
    text_message = i_message.
  ENDMETHOD.
  METHOD if_amc_message_receiver_binary~receive.
    binary_message = i_message.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  DATA(receiver) = NEW message_receiver( ).

  TRY.
      cl_amc_channel_manager=>create_message_consumer(
          i_application_id = ‘DEMO_AMC’
          i_channel_id    = ‘/demo_text’
          )->start_message_delivery( i_receiver = receiver ).
    CATCH cx_amc_error INTO DATA(text_exc).
      cl_demo_output=>display( text_exc->get_text( ) ).
  ENDTRY.

 

  TRY.
      cl_amc_channel_manager=>create_message_consumer(
          i_application_id = ‘DEMO_AMC’
          i_channel_id    = ‘/demo_binary’
          )->start_message_delivery( i_receiver = receiver ).
    CATCH cx_amc_error INTO DATA(binary_exc).
      cl_demo_output=>display( binary_exc->get_text( ) ).
  ENDTRY.

 

  WAIT FOR MESSAGING CHANNELS
      UNTIL receiver->text_message  IS NOT INITIAL AND
            receiver->binary_message IS NOT INITIAL
      UP TO 60 SECONDS.

  cl_demo_output=>begin_section( receiver->text_message ).
  cl_demo_output=>display_xml( receiver->binary_message ).

Similar to sender objects you create consumer objects. But instead of sending a message with SEND, you use START_MESSAGE_DELIVERY that (in spite of its name) defines the AMC reciever phase and registers receiver objects for the messages of channels. The class of an receiver object can be any class that implements the appropriate interfaces as shown for the local class RECEIVER of the example. Last but not least, you need to halt your process in order to wait for messages. To do so you use the new variant WAIT FOR MESSAGING CHANNELS UNTIL of the now obsolete WAIT UNTIL statement (another “new” variant is WAIT FOR ASYNCHRONOUS TASKS UNTIL).

You can run this program now and … again nothing happens, duh!  No, no, just joking: if you run the above sender program within 60 seconds on any application server of the same AS ABAP, you will see the messages being received by the consumer program.

Not too impressive? Well, it wasn’t possible before (mind the refresh buttons) and it can be connected to APC …

ABAP Push Channels (APC)

ABAP Push Channels (APC) allow an event based communication between ABAP programs and the internet using the WebSocket Protocol.

An APC is a repository object also maintained in SE80 or directly using transaction SAPC. When you create an APC, you automatically create a node in ICF (the internet address of the channel) and an APC handler class where you implement the behavior of the channel. At least the two methods ON_START and ON_MESSAGE of the interface IF_APC_WS_EXTENSION have to be redefined.In order to make the WebSocket comminication independent from an application server, you can simply connect several or all application servers of an AS ABAP to the APC by binding the APC to AMC. The necessary AMC methods are handled by the APC framework.

Example

The following is a HTML page that you can display in any browser supporting WebSockets:

<html>

<head>

<script type=”text/javascript”>

var ws;function WebSocketDemo(para)

{

  if (“WebSocket” in window)

  {

    if (para == “open” )

    {

        ws = new

            WebSocket(“wss://ldai2uia.wdf.sap.corp:44300/sap/bc/apc/sap/demo_apc?amc=x”);

    };

    if (para == “send” )

    {

        ws.send( “>” + document.getElementById(“input”).value + “<” +

                ” from ” + window.location.host + window.location.pathname );

    };

    if (para == “close” )

    {

        ws.close( );

    };

    ws.onopen = function()

    {

        alert(“WebSocket opened”);

    };

    ws.onmessage = function (evt)

    { 

        var received_msg = evt.data;

        alert(evt.data);

    };

    ws.onclose = function()

    { 

        alert(“WebSocket closed”); 

    };

  }

  else

  {

    alert(“Browser does not support WebSocket!”);

  }

}

</script>

</head>

<body style=”font-family:arial;font-size:80%;”>

<h3>

  WebSocket Communication with ABAP Push Channel

</h3>

<p>

  <a href=”javascript:WebSocketDemo(‘open’)”>Open WebSocket</a>

</p>

<p>

  <form name=”input”>

  <a href=”javascript:WebSocketDemo(‘send’)”>Send message to APC</a> 

    <input name=”input” id=”input” value =”Hello APC!”

          type=”text” size=”20″ maxlength=”20″ >

  </form>

</p>

<p>

  <a href=”javascript:WebSocketDemo(‘close’)”>Close WebSocket</a>

</p>

</body>

</html>

The body is simply for displaying some forms:

WebSocket.png

The interesting part is the handling of the forms in the JS-function WebSocketDemo. Admittetly I have stolen large parts of that function from a WebSocket tutorial from the Web and adjusted it to my needs (isn’t it good to follow standards?). I mainly inserted the URL “…/sap/bc/apc/sap/demo_apc” of an APC on an application server of an AS ABAP (the URL form field ?amc=… is introduced in order to steer the binding of the APC to an AMC). You see that the JS opens an WS object, sends and receives messages using that object, and closes the WS object. That’s all.  Now  for the ABAP part (which I could not copy but had to learn the hard way as always when I explore the “fresh from the lab” stuff landing on my desk …).

I have an APC application named APC_DEMO in my demo package. For this APC, the above mentioned URL was generated in ICF and I could readily use it. All I had to do was to implement the above mentioned methods in the also generated class CL_APC_WS_EXT_DEMO_APC.

METHOD if_apc_ws_extension~on_start.
  TRY.
      DATA(amc_flag) =
          COND abap_bool( WHEN to_upper(
                              i_context->get_initial_request(
                              )->get_form_field( i_name = ‘amc’ )
                              ) = abap_true
                          THEN abap_true
                          ELSE abap_false ).
    CATCH cx_apc_error.
      amc_flag = abap_false.
  ENDTRY.

  IF amc_flag = abap_true.
    TRY.
        i_context->get_binding_manager(
          )->bind_amc_message_consumer(
            i_application_id =  ‘DEMO_AMC’
            i_channel_id    = ‘/demo_text’ ).
      CATCH cx_apc_error INTO DATA(exc).
        MESSAGE exc->get_text( ) TYPE ‘X’.
    ENDTRY.
  ELSE.
    “Default behavior
  ENDIF.
ENDMETHOD.

If the form field AMC was passed wiith “x”, the APC is bound to AMC.

METHOD if_apc_ws_extension~on_message.
  TRY.
      DATA(amc_flag) =
          COND abap_bool( WHEN to_upper(
                              i_message->get_context(
                              )->get_initial_request(
                              )->get_form_field( i_name = ‘amc’ )
                              ) = abap_true
                          THEN abap_true
                          ELSE abap_false ).
    CATCH cx_apc_error.
      amc_flag = abap_false.
  ENDTRY.

  TRY.
      DATA(msg) = |message from APC on { sy-host }: Received “{ i_message->get_text( ) }”|.
      IF amc_flag = abap_true.
        CAST if_amc_message_producer_text(
              cl_amc_channel_manager=>create_message_producer(
                i_application_id = ‘DEMO_AMC’
                i_channel_id    = ‘/demo_text’ )
        )->send( i_message = `AMC-` && msg  ) ##NO_TEXT.
      ELSE.
        DATA(message_manager) = i_message->get_context( )->get_message_manager( ).
        DATA(message) = message_manager->create_message( ).
        message->set_text( `Default ` && msg ) ##NO_TEXT.
        message_manager->send( message ).
      ENDIF.
    CATCH cx_amc_error cx_apc_error INTO DATA(exc).
      MESSAGE exc->get_text( ) TYPE ‘X’.
  ENDTRY.
ENDMETHOD.

If the form field AMC was passed wiith “x”, the APC lets the bound AMC do the work. Otherwise, an APC message manager is created and used. Not too complicated, huh?

If APC is bound to AMC, the messages sent by ON_MESSAGE is not just received by the webpage that sent a message, but by all webpages currently connected using the push channel, independent from the application server. In fact, those webpages receive all messages sent using this AMC, e.g. also those sent from the sender of the AMC example above! You can place external brealpoints in the method implementations to control the behavior!

FireFox.png

Now imagine JavaScript functions that do more than simple alerts and you are not too far from the ping pong above …

You can test the demos including the ping pong directly from the keyword documentation (as of 7.40, SP05).

 

More about

Fore more information see Masoud’s article!

.

.

To report this post you need to login first.

13 Comments

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

  1. Jelena Perfiljeva

    Horst – good stuff (as long as it really works 🙂 – after dealing with the event-based jobs the word ‘event’ make me twitch). Would you consider posting this in ABAP space, so that it has more visibility?

    Thank you.

    (0) 
    1. Horst Keller Post author

      Hi Ivan,

      In ABAP channels access rights are defined in the channel definition or in administration tools. In AMC you define a white list of programs/classes that are authorized to use the channels and of acrivities that are allowed for these programs (send, receive, bind to APC).  In APC the default setting ist, that you can handle a WebSocket only, if its file stems from the same domain. For all cross domain activities again a whitelist must be maintained (in database table APC_CROSS_ORIGIN with transaction SAPC_CROSS_ORIGIN), which is controlled by an authorzation obejct. On administration level, WebSocket communication can be switched on or off in the ICM.

      Best

      Horst

      PS: APC can be connected to a virus scan profile for the Virus Scan Interface VSI.

      (0) 
  2. Paulo Dinis

    Great news! I already read about pusher one year ago and I was wondering why there was no SAP standalone solution to implement web sockets. So here it is… ABAP channels!

    Next step: get a 7.4 release!

    Nice post.

    (0) 
    1. Horst Keller Post author

      At least since NW 7.40, SP05 , the game is delivered as a demo.

      The program DEMO_APC_PING_PONG (my humble contribution) can be used for starting the game. It calls the ICF-URLs  …/sap/bc/apc_test/ping_pong/game for creating the field (handler is CL_HTTP_EXT_PING_PONG_PAGE) and  …/sap/bc/apc_test/ping_pong/player for creating browser based players (handler is CL_HTTP_EXT_PING_PONG_PLAYER) . SAP-GUI-Players can be created by calling program RS_APC_PING_PONG.

      Besides the code of the above objects you also need the APCs as repository objects,

      (0) 
      1. M. A. van Harten

        Tanks Horst,

        I found al most everything.

        Only your great contribution (the demo program DEMO_APC_PING_PONG) I cannot find anywhere.

        Is there a system available where I can find this program?

        (0) 
        1. Horst Keller Post author

          No really, DEMO_APC_PING_PONG is only a convenience program that calls the game programmed by the real geeks. It is definitely available with 7.40, SP05.  I attached the source code to the blog. When using it before SP05, be aware that  cl_http_server=>get_location might not have a returning parameter yet and that you have to concatenate the exporting parameters yourself.

          (0) 
  3. Krishna Kishor Kammaje

    Hi Horst,

    Thanks for the document. I am 740 SP4. I created an APC application and redefined Onstart and Open methods. When I try to connect using javascript, I see that connection fails. Google chrome says that unexpected error 500. I see that it is failing because of XSRF token unavailability. How do I get XSRF token for a ws connection in javascript?

    Thanks in advance

    Krishna

    (0) 
    1. Masoud Aghadavoodi Jolfaei

      Hi Krishna,

      in earlier releases < 740 SP5 we used the XSRF token to ensure the same origin policy in APC. We have changed this behviour in 740 SP5 and use instead the RFC 6454 – The Web Origin Concept.

      To get the WebSocket URL from backend including XSRF token I have provided the method cl_apc_ws_utility=>get_access_url which can be used in an HTTP server session. Just have a look into my sample code in class CL_HTTP_EXT_PING_PAGE belonging to the HTTP ping test http://<host>:<port>/sap/bc/apc_test/ping which uses the APC application PING to establish a simpel chat functionality. In order to get the ping test running you have to activate both paths /sap/bc/apc/apc_test/ping and /sap/bc/apc/sap/ping in SICF transaction before.


      Good luck !

      Masoud

      (0) 
      1. Sanchez Wang

        Hi,

        When i try to access the index.html of sample application AOC which is in subpackage SAOC_UI5. I got following error.

        The termination occurred in system with error code 403 and for the reason Forbidden.


        May u you have any hint about the root cause of this? I activated related service in SICF already.And i have release sapka75012.


        Thanks for your reply in advance.

        (0) 
        1. Masoud Aghadavoodi Jolfaei

          Hi Sanchez,

          for AOC UI5 application several SICF services has to be activated. Just check out the blog ABAP Channels Examples section “UI5/Fiori version of AOC” and the “Prerequisite” part.

          Usually the 403 responses are cached in the ICM cache of the respective ABAP application server. In this case reset the ICM cache on the target application server via transaction SMICM.

          Cheers,

          Masoud

          (0) 

Leave a Reply