Since ABAP has garbage collection, most of the time just clearing references is enough to dispose of resources. Sometimes, however, a close( ) call is also required.

HTTP Connections

Transaction SM59 allows HTTP connections to be configured:

SM59 - Technical Settings

The proxy settings are obviously handy, but especially nice is the option for configuring logon credentials:

SM59 - Logon & Security

This removes the responsibility of handling sensitive credentials in ABAP code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
" Factory method for creating HTTP Clients
cl_http_client=>create_by_destination(
  EXPORTING
    destination = 'GOOGLE' " Destination configured in SM59
  IMPORTING
    client      = DATA(lo_http_client)
).


" IF_REST_CLIENT has a nicer interface than IF_HTTP_CLIENT
DATA(lo_rest_client) = CAST if_rest_client( NEW cl_rest_http_client( lo_http_client ) ).


" Any logon details will be automatically added to the call
lo_rest_client->get( ).

Anyone with experience using HTTP APIs will notice one shortcoming: there’s no support for any other method of authorization than basic authorization. So if the endpoint requires a bearer token or a custom HTTP header, SM59 logon settings are not much use to us.

This requirement for other methods of authorizations was exactly the case with a couple different developments required by a customer. My solution, along the lines of dependency injection, was to create a small service from which clients could request REST clients:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
" ZIF_REST_CLIENT was just a proxy for
" a IF_REST_CLIENT instance, with the addition
" of checked exceptions
INTERFACE zif_rest_client.
...
ENDINTERFACE.


INTERFACE zif_rest_client_service.

  METHODS create_rest_client_for_path
    IMPORTING im_path TYPE string
    RETURNING VALUE(r_result) TYPE REF TO zif_rest_client.

ENDINTERFACE.


"""


DATA(lo_rest_client_service) = zcl_rest_client_service=>create_instance(
  im_destination   = 'GOOGLE'
  im_authorization = 'Bearer some_token'
).


DATA(lo_client) = NEW zcl_some_client( io_rest_client_service = lo_rest_client_service ).

This service hid any authorization -related concerns from clients. Thus, after the service instance had been created with the required configuration in place, it could just be injected into clients that needed to access external HTTP servers. This injection also allowed those clients to be unit testable by mocking the HTTP client service and the REST clients it produced.

Out of Memory

Everything worked fine for a while, until a mysterious error message started to appear:

No memory for processing HTTP, HTTPS, or SMTP query

We dealt with requests and responses that carried fairly small amounts of text data, so I was confused as to how an SAP Application Server could ever run out of memory with our usage. Turns out that the content was not the problem, but the number of HTTP clients we were creating.

My ZIF_REST_CLIENT_SERVICE created a new IF_HTTP_CLIENT instance for each HTTP request. IF_HTTP_CLIENT’s existence, however, is special: if it has been used in a HTTP call, it’s not enough to just clear references to it. To free all resources associated with it, close( ) needs to also be called.

In my defense, I’d like to direct the jury to the description of the close( ) method:

IF_HTTP_CLIENT methods

To me the description reads more like it is intended for closing a connection before the response has been received. Even the documentation of the whole interface does not mention the special role of the close( ) method. Only the longer documentation for the method spells it out clearly:

close( ) method documentation

Adding close( ) calls to properly free resources got rid of the out of memory error messages, but this was the first case I encountered where garbage collection was not enough to get rid of ABAP objects. The ambiguous/non-specific error message was also not especially helpful in resolving the issue.

Guidelines / Consistency

Ultimately the question we’re left with is “how to make APIs hard to misuse”. Because ABAP objects only rarely need to be disposed of in an explicit manner, there are little if any established hints as to when this should take place. In IF_HTTP_CLIENT the special discard method is called close( ) and described as Close of HTTP connection; in GUI-related objects it is free( ) with description Destructor. I’d argue that the latter is a stronger signal as to the special nature of the method. But in CL_GUI_ALV_GRID, free( ) is just one among 400 other methods, 90 of which are public. Only the fact that all GUI-related objects have (or rather, inherit) the free( ) method establishes it as special. For IF_HTTP_CLIENT, no such family of related classes and thus no such established consistency, exist.

One option might be to have a global interface somewhat like IDisposable in C# that would act as a signal that the object needs to be explicitly disposed of.

Destructors don’t exist in ABAP, and might not even be good enough if the garbage collection did not execute between HTTP calls.