Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
horst_keller
Product and Topic Expert
Product and Topic Expert

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

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 :grin: .

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:

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!

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!

.

.

13 Comments