RFC servers

The RfcServer object allows you to add RFC Server functionality to your program. RFC Servers are called from the SAP system and can process and return data.

Introduction to RFC servers

In some situations, the flow of events does not originate from the client application, but from the SAP system itself. For example, the SAP system may send IDOCs to notify another application about data changes, or a SAP transaction may delegate calculations or acquire additional data to/from a third party system. When for these kind of communications RFC is used, the application in question needs to implement a RFC server.

In SAP, possible destinations for remote function calls are called „RFC destinations“. The list of available RFC destinations can be administered with transaction SM59. For each RFC destination in SM59 (for Rfc Connector, only the „TCP/IP connections“ subtree is relevant), a Program ID and an activation type is maintained. For RFC servers created with Rfc Connector, the activation type is always „Registered Server Program“

When the RFC server is started, it registers with the SAP system and the Program ID, which must match the one defined in SM59. Through this registration, the SAP system knows the address of the listener(s) and is now able to dispatch RFC calls to them. You can check available listeners for a specific RFC destination with the „Connection Test“ button in SM59, and you can get a list of all listeners currently registered with transaction SMGW (use „Go To“->“Logged In Clients“)

Implementing an RFC server

To implement an RFC server which can receive function calls from SAP, the following steps are necessary:

  1. Create a server object and set the necessary parameters: GatewayHost, GatewayService, ProgramID
  2. Create or import one or more FunctionCall prototypes and install them using the InstallFunction method
  3. Subscribe to the Logon event to allow or forbid incoming calls
  4. Subscribe to the IncomingCall event
  5. Run the server and wait for incoming calls

1. Create a server and set the necessary parameters

Create an instance of RfcServer and set the necessary parameters: ProgramID determines the program id under which to register (as configured in SM59), GatewayHost is the host name (or IP) of the SAP system to register with, and GatewayService determines the system number (port) of this SAP system:

Note: The parameter GatewayService is given in form of a string starting with „sapgw“ followed by the 2-digit system number. For example, SYSNR=00 in a connection string corresponds to sapgw00, SYSNR=01 corresponds to sapgw01, and so on.

Dim WithEvents srv As RfcServer 'WithEvents is important here
srv = New RfcServer
srv.ProgramID = "ZRFCCTEST"
srv.GatewayHost = "mysaphost"
srv.GatewayService = "sapgw01"

2. Create a function call and install it to the server

To be able to decode the incoming parameters, Rfc Connector needs to know the signature/prototype of the RFC function(s) which are going to be called. In this case, we just import it from the server, but it would also be possible to load it from serialized XML or build it manually:

' import function definitions
Dim Session as New RfcSession
Dim prototype as FunctionCall, fn2 As FunctionCall
Session.RfcSystemData.ConnectString = "SAPLOGON_ID=N4S"
Session.LogonData.Client = "001"
Session.LogonData.User = "user"
Session.LogonData.Password = "password"
Session.Connect()
If Session.Error Then
    Console.WriteLine("Error: " + Session.ErrorInfo.Message)
End If    
prototype = Session.ImportCall("BAPI_FLIGHT_GETLIST")
Session.Disconnect()

' now register them with server
srv.InstallFunction(prototype)

3. Subscribe to the Logon event

Now we add a handler for the Logon event, which is used to determine whether a request is allowed or not. For this example, we just check the username:

Private Sub srv_Logon(ByVal li As RFCCONNECTORLib.RfcServerLogonInfo) Handles srv.Logon
  If li.User = "DEVELOPER" Then
    li.RequestAllowed = True
  Else
    li.RequestAllowed = False
  End If
End Sub

4. Subscribe to the IncomingCall event

In the handler for the IncomingCall event, the actual request processing is done. To keep this example simple, we reply with some static data only (real-world examples would be, of course, much more complex):

If parameters are marked „optional“ in the function module definition, they may not be set by the caller. Therefore, for optional parameters use the HasKey-function to check whether they are set or not before accessing them. Otherwise, you would get an exception.

Private Sub srv_IncomingCall(ByVal fn As RFCCONNECTORLib.FunctionCall) Handles srv.IncomingCall
    Select Case fn.Function
        Case "BAPI_FLIGHT_GETLIST"
            Dim airline As String, dest_from As String, dest_to As String
            If fn.Importing.HasKey("AIRLINE") Then
                airline = fn.Importing.HasKey("AIRLINE")
            Else
                airline = "LH"
            End If

            If fn.Importing.HasKey("DEST_FROM") Then
                dest_from = fn.Importing.HasKey("DEST_FROM")
            Else
                dest_from = "SFO"
            End If

            If fn.Importing.HasKey("DEST_TO") Then
                dest_from = fn.Importing.HasKey("DEST_TO")
            Else
                dest_from = "JFK"
            End If

            ' add some static rows 
            Dim r As RfcFields, dt As DateTime
            dt = DateTime.Today
            For i = 1 To 10
                r = fn.Tables("FLIGHT_LIST").Rows.AddRow()
                r("AIRLINEID").value = airline
                r("AIRPORTFR").value = dest_from
                r("AIRPORTTO").value = dest_to
                r("CONNECTID").value = i
                r("FLIGHTDATE").value = dt.AddDays(i)
            Next
        Case Else
            ' we don't know about this function, so we raise an exception
            fn.RaiseException("SYSTEM_ERROR")
    End Select
End Sub

5. Run the server and wait for incoming calls

Now that we have implemented our handlers, it is time to start the server and wait for incoming calls (this is done in the main method). Note that the server always runs in another thread, so Run() will always return immediately after registering with the SAP system. In this example, we will run the server until the user presses a key:

If srv.Error Then
    Console.WriteLine("Error: Could not start server.")
    Console.WriteLine(srv.ErrorInfo.Message)
    Exit Sub
End If

Console.WriteLine("Server running. Press key to stop...")
Console.ReadLine()

srv.Shutdown()

Console.WriteLine("Server exited. Press key to leave...")
Console.ReadLine()

Handling errors while the server is running

It may happen that the server encounters an error (for example, a temporary network problem, or the SAP host being restarted) while listening for requests.

To handle these cases, applications should subscribe to the ServerError event, which offers also an option to just restart the server (which is recommended for servers which are designed to run unattended for a longer time):

Private Sub srv_ServerError(ByVal ei As RFCCONNECTORLib.RfcServerErrorInfo) Handles srv.ServerError
    ' log the error to console
    Console.WriteLine(ei.Message)
    ' restart the server
    ei.Restart = True
End Sub