BSP a Developer’s Journal Part XIV – Consuming WebServices with ABAP
You might be thinking: how is consuming WebServices in ABAP really related to BSP. Is it because the WebAS and the ICM (Internet Communication Manager) are at the core of both technologies? Or perhaps it is because you might describe Consuming WebServices in ABAP as BSP turned inside out? When it comes right down to it, I included this in BSP Weblog because for me using WebServices just goes hand-in-hand with the applications that I happen to be developing in BSP. But don’t feel that this technology is limited to BSP. In fact all the samples I am going to share today run within good old traditional ABAP. There is even one with – gasp – just plain List Output (talk about combining old technology with new!).
First off there is no easy, good way of consuming SOAP WebServices in 620. There is a client API realized as an ABAP Class Library. But if you read the SOAP Processor section on the service marketplace , you see that this method in 620 is basically incompatible with release 640. You have to work directly with the SOAP request at a fairly low level in the 620 API because proxy generation is not supported. All this and then you read the statement:
applications based on this interface therefore will face a considerable migration workload. Automatic conversion will not be possible.
Well that was enough to convince me to wait until 640 for full SOAP WebServices.
I was faced with a problem. I have a requirement to build an application that sends SMS messages to cell phones in Poland. Our SMS provider was only giving me the option of interfacing via a SOAP WebService. I needed the working interface up in running by September 2004. But at this time, our 640 upgrade was more than 6 months away. I didn’t even have a copy of the install media yet! But as people in the area I live in are prone to say, there is more than one way to skin a cat (makes you wonder what we do for fun around here). In 620 I may not have a good SOAP WebService client, but I do have a HTTP Client. Meaning I can make HTTP requests and process the response from ABAP. So why not simplify the interface by removing the SOAP. We make a request and on the URL we specify certain parameters (SMS Number, Priority, Message, etc). The service provider processes the request and sends us back a confirmation number and status in the response object. Little did we know at the time that we were flirting in the SOAP vs. REST area of WebServices. Now I have never been the type of person to get involved in theoretical debates about the potential uses of technology. I’m more of Git-‘R-Done kind of guy. Does the solution meet the requirements, budget, and timeline? Can I support it after we go live. If yes – then I say go for it. If you are interested in the SOAP vs. REST debate you can find plenty of articles on the web (and a few right here in SDN).
Before we could really get started with this solution, we needed to configure a few things in the WebAS of course. First we will be passing HTTP(s) out of our Corporate Network, across the public Internet, and finally reaching our Service Provider (hopefully!). That means that like most corporations, we needed to configure our proxy settings for passing through the firewall. You can find these settings in Transaction SICF under GOTO->HTTP Client Proxy. You can probably just copy the same settings that you find in your web browser to complete these settings. If not have a chat with your network administrator.
We are going to be using HTTPS for our communication. Therefore we need to add our provider’s certificate to our Trust Manager. We can do this from transaction STRUST. Choose Environment->SSL Client Identities from the menu. This screen allows us to create a short SSL ID that we will attach our certificate to. We will also use this same ID later in our ABAP code to include this certificate in our HTTP Processing. We can return to the main screen in STRUST and upload their certificate. I had already navigated to the HTTPS URL that we would be using with my Internet Explorer browser. Therefore all I had to do was go into Certificates Listing in IE and Export the certificate. If you export as DER encoded binary X.509 (.CER) this matches up to the import option in SAP of File Format Binary.
You then should see your certificate under your SSL ID with a green light.
We are ready to look at the coding now. We will break it down into little parts so that is can be easily digested. First I implemented this functionality as a function module so that it can be called via RFC from our 46C R/3 system. I take in the SMS number, the Message Class, the Message, and the Priority as importing parameters. I then pass back the unique SMS confirmation number, the status and the entire HTTP response as a string.
*” VALUE(SMS_NUMBER) TYPE AD_PAGNMBR
*” VALUE(CLASS) TYPE CHAR1 DEFAULT 1
*” VALUE(MESSAGE) TYPE STRING
*” VALUE(PRIORITY) TYPE CHAR1 DEFAULT 3
*” VALUE(SMS_ID) TYPE ZZES_SMS_ID
*” VALUE(STATUS) TYPE ZZES_SMS_RESULT_STATUS
*” VALUE(P_CONTENT) TYPE STRING
I wanted to make it easy to support different SMS providers or to make changes to the URL and SSL_ID; so ended up setting up everything in a configuration table.
So we can then start of function module with a little bit of code to read the configuration settings from the config table.
****Lookup the setting for Webt data: page_srv type zes_sms_page_srv. select single * from zes_sms_page_srv into page_srv where serv = ‘WEBT’.
Next up I want to perform some checks on the input data. First I will check to make sure the message isn’t too long. Next up I want to check for Polish Characters. Even though we are going through a Polish SMS provider, they asked us not to send any Polish National Characters. So we will use some SAP function modules to check the codepage of the string and then transliterate any national characters back to ASCII7. Finally we will replace any + in the SMS number and remove any spaces.
****Check the Lenght of the Message data: msg_len type i. msg_len = numofchar( message ). if msg_len > page_srv-max_chars. status = ’21’. raise message_too_long. endif.****Check Character set of the incomming message data: is_ok type scpuserec. call function ‘SCP_CHECK_CHARSET_OF_STRING’ exporting text = message langu = ‘E’ importing is_ok = is_ok exceptions internal_error = 1 fields_not_char = 2 overflow = 3 others = 4. if sy-subrc <> 0 or is_ok is initial.****Lookup the Codepage for Polish data: i_codepage type cpcodepage. call function ‘SCP_CODEPAGE_FOR_LANGUAGE’ exporting language = ‘L’ importing codepage = i_codepage exceptions no_codepage = 1 others = 2. call function ‘SCP_REPLACE_STRANGE_CHARS’ exporting intext = message in_cp = i_codepage importing outtext = message exceptions invalid_codepage = 1 codepage_mismatch = 2 internal_error = 3 cannot_convert = 4 fields_not_type_c = 5 others = 6. endif.****Check the recipient Number replace all occurrences of ‘+’ in sms_number with ‘ ‘. condense sms_number no-gaps.
Next up is where things get really interesting. We will build our full Request URL by adding on all of the outbound parameters. We create the client object for this URL.
data: client type ref to if_http_client, url type string.****Build the Sending URL concatenate page_srv-send_url `&Message=` message `&Class=` class `&Number=` sms_number `&Priority=` priority `&Project=` page_srv-project_name `&Sendingnumber=` page_srv-sending_number into url.****Create the HTTP client call method cl_http_client=>create_by_url exporting url = url ssl_id = page_srv-ssl_id importing client = client exceptions others = 1.
Next up we set our request type to ‘GET’ and start the HTTP communications.
****Set the Request type to GET client->request->set_header_field( name = ‘~request_method’ value = ‘GET’ ). “#EC *****Make the call client->send( ).
Now we are ready to receive the response back. We read it in character format. We then pull out the return parameters from this response block.
****Receive the Response Object call method client->receive exceptions http_communication_failure = 1 http_invalid_state = 2 http_processing_failed = 3 others = 4.****Get the response content in Character format p_content = client->response->get_cdata( ). data: result_string type string, id_string type string, junk type string.****Pull out the Result and the ID split p_content at `&` into result_string id_string. if result_string cs `Result`. split result_string at `=` into junk status. else. raise unknown_error. endif.
In the end we have fairly simple approach to what could have been a complex problem. We leverage the ability of the WebAS to act as an HTTP client and create an end result that functions very much like a WebService.
If you really had your heart set on consuming SOAP WebServices from ABAP, you aren’t out of luck. WebAS 640 comes along and makes the process relatively painless by offering the ability to generate proxy classes. Since this technology is so new, I thought I might just build and walkthough a complete example here. I wanted to use a public webservice that was available for free for several reasons. First I wanted to demonstrate how flexible the technology was. Second I wanted anyone with a 640 system to be able to recreate the steps in this weblog on their own with the same webservice. I found a nice collection of webservices at the following web address: www.xmethods.com . I finally decided upon a simple service that returns book information when given an ISBN. It even gives us the current price of the book at several on-line retailers. The Service Description can be found here: http://www.winisp.net/cheeso/books/books.asmx .
Now that we have our webservice picked out, we are ready to generate our ABAP Proxy. To get the process started, we turn to our old friend SE80. From the Enterprise Services Tab, we are going to select Client Proxy and then hit create.
Next up we have to choose where our WSDL file is going to come from. We could connect directly to a UDDI server or XI repository and query for our object. Or we might have saved the WSDL definition to a file on our local machine. But in this case we are going to connect directly to the WSDL definition using the URL that we got from our on-line search.
Now we are asked what Package (Development Class for those you upgrading from 46C or lower) we want to place the generated object in as well as what prefix we want to use. I’m just playing around so I am going to make the object Local Private by putting in my $TMP package. To follow SAP naming standards, as well as my company’s standards, I will get the object a prefix of ZES_ (In case you are wondering – the object starts with Z to follow SAP’s naming standard for customer development. The next letter E standard for Electronics – the division I work for. Finally the S stands for Basis – the application area that this development belongs to.)
We have our generated Client Proxy. The following are some screen shots of what this should look like in SE80. You can see that not only do we have an ABAP class, but the process also generated structures and table types to represent all the importing and exporting data.
There is only one last thing we need to do to our Client Proxy before we are ready to use it: we need to configure a Logical Port for it. We can do this in transaction LPCONFIG. We can configure more than one Logical Port and specify the one we want at runtime. However for this solution we will just configure one port and make it the default.
Inside the definition of the Logical Port we can adjust settings for the Client Proxy such as the URL we going to call, the logging, State Management, etc. Since this is the default Logical Port, we will just save it with all the settings that were imported from the WSDL definition.
We can then return to SE80 and perform a test on the WebService by hitting F8. We then get a dialog that allows us to set parameters for our test.
We then choose which method we want to invoke. We are going to be using the GET_INFO method later in our example, so let’s test that.
Hopefully you get a success message like the following:
Once everything checks out OK in the test tool, we are ready to start programming against our Client Proxy. The following program example has one parameter for supplying the ISBN. We then take this parameter and use it to fill out the request object for our Client Proxy. After the call to the Client Proxy, all the returned data is contained in our response object. The response object has the format of the generated table types and structures as defined in the WSDL file. We can loop through this data and process as we see fit. For this case we are just going to output a simple ABAP list.
PARAMETER: p_isbn(100) TYPE c OBLIGATORY.
Reference variables for proxy and exception class
lo_clientproxy TYPE REF TO zes_co_looky_book_service_soap,
lo_sys_exception TYPE REF TO cx_ai_system_fault,
Structures to set and get message content
ls_request TYPE zes_get_info_soap_in,
ls_response TYPE zes_get_info_soap_out.
****Set the input parameter ISBN into the Request of the SOAP Object
ls_request-isbn = p_isbn.
****Create the Proxy and Clall it.
CREATE OBJECT lo_clientproxy.
CALL METHOD lo_clientproxy->get_info
input = ls_request
output = ls_response.
CATCH cx_ai_system_fault INTO lo_sys_exception.
****Write Out the Basic Information
WRITE: / ‘ISBN:’, ls_response-get_info_result-isbn.
WRITE: / ‘Title: ‘, ls_response-get_info_result-title.
WRITE: / ‘Author: ‘, ls_response-get_info_result-author.
WRITE: / ‘Publish Date: ‘, ls_response-get_info_result-pubdate.
WRITE: / ‘Publisher: ‘, ls_response-get_info_result-publisher.
WRITE: / ‘Format: ‘, ls_response-get_info_result-format.
****Loop through the listing of Vendor Prices and output each one
What I showed you here today is the tip of the iceberg for this technology. The possibilities for connecting applications through WebServices (even with different implementing technologies) are quite exciting. This is just one of the many new areas of NetWeaver that we all have to look forward to.