Consuming Webservices with tag in WSDL using ABAP

Consuming Webservices with tag in WSDL using ABAP


Consuming Webservices with tag in WSDL using ABAP

Have you ever generated a consumer proxy for a .NET web service and ended up with an empty output. I did and I found a few others on SDN who did. After you look around some more, you might find that the consumer proxy generation doesn't support certain XML tags in the WSDL. One of these Tags is tag. This blog gives you a step-by-step on how to consume such a web service.

Background

There are several weblogs and help files describing the process of how to consume a web service in ABAP. I followed one of these guides to consume a web service that was generated using .NET in our company. The web service needs an input date and produced a table output of five fields per row. Step 1 was to generate a consumer proxy using the URL for the WSDL of the .NET web service. The process worked like good cake and I had a consumer proxy with the ABAP class generated and activated. I was anxious to test it. I clicked the test icon in SE80 to realize that I needed a logical port. With some more help from the useful blogs on SDN, I was running SOAMANAGER to create my logical port. This was the first stumbling block where the logical port creation based on URL didn't work and the SDN blogs didn't give me a solution. I was able to create the logical port using the manual configuration option (details to follow in a future blog). Back to testing the consumer proxy, I fill in the date in system generated XML input template and Execute. No errors or SOAPFaultCode1 messages finally. I had done it. I clicked on the Output tab to see what I got back. My excitement quickly fizzled away to see a blank page. Then I checked the Original Response tab. The XML response from the web service was all there. Why was the output blanks and how do I get to the response?

Root Cause for blank output

A consumer proxy does three main jobs:

  • 1. Converts input parameters from ABAP format to XML format
  • 2. Invokes the web service with XML request and receives output in XML format
  • 3. Converts received XML response into ABAP data types

The ABAP to XML and XML to ABAP conversions of Step 1 and Step 3 are done using Transformations (extensive help available in SAP help). These transformations are generated during the consumer proxy generation process. In my scenario above the transformation for Step 3 did not give me the output I was looking for. It turns out that the cause of the problem was the web service WSDL itself. The WSDL used tag in the description of the output message. tags are used to make the output message description extensible, but are not supported by the consumer proxy generation wizard. I did not have the option to ask the .NET team to change their web service, so I decided to use the consumer proxy to get the XML response from the web service and translate the XML response to ABAP using a simple transformation myself.

Solution - Call a webservice with XML input and get XML output back

The solution doesn't go into the details of XML programming or input/output conversion of ABAP to XML or XML to ABAP. There are several blogs around that topic on SDN. What I describe here is an ABAP class that calls the consumer web service with input XML request and receives the XML response. The XML response can be processed by the calling ABAP program as needed.

ZWS_CALL ABAP Class

Create a class ZWS_CALL with one method as shown below. No attributes are needed.

Method signature

Importing

CLASS

TYPE

SEOCLNAME

Importing

METHOD

TYPE

SEOCLNAME

Importing

LOGICAL_PORT

TYPE

PRX_LOGICAL_PORT_NAME

Importing

INPXML

TYPE

XSTRING

Request XML

Exporting

RESXML

TYPE

XSTRING

Response XML

Exception

CX_AI_SYSTEM_FAULT

Application Integration: Technical Error

Method Code

method WS_CALL.
DATA:
lr_proxy TYPE REF TO cl_proxy_client,
lr_reader TYPE REF TO if_sxml_reader,
lr_writer TYPE REF TO cl_sxml_string_writer,
lr_ws_payload TYPE REF TO if_ws_payload,
lr_prot_payload TYPE REF TO if_wsprotocol_payload,
lr_request_part TYPE REF TO if_sxmlp_data_st,
lr_response_part TYPE REF TO if_sxmlp_data_st,
lt_parameter TYPE abap_parmbind_tab,
lr_classdescr TYPE REF TO cl_abap_classdescr,
lr_error_part TYPE REF TO if_sxmlp_data_st,
lr_app_fault TYPE REF TO cx_ai_application_fault,
extended_xml_handling TYPE abap_bool,
org_request_data TYPE xstring,
org_response_data TYPE xstring,
response_data TYPE xstring,
error_data TYPE xstring,
exception_class_name TYPE string.

* request_data = inpxml.
cl_proxy_st_part=>create_for_clas_method(
EXPORTING
class = class
method = method
for_serialize_request = abap_false
for_deserialize_request = abap_true
for_serialize_response = abap_true
for_deserialize_response = abap_false
extended_xml_handling = extended_xml_handling
IMPORTING
request_part = lr_request_part
response_part = lr_response_part
param_tab = lt_parameter
).

TRY.
* map xml to abap data
lr_reader = cl_sxml_string_reader=>create( inpxml ).
lr_request_part->deserialize( reader = lr_reader ).

* create proxy
CREATE OBJECT lr_proxy
EXPORTING
class_name = class
logical_port_name = logical_port.

lr_prot_payload ?= lr_proxy->get_protocol(
if_wsprotocol=>payload ).

lr_prot_payload->announce_payload_consumption( ).

* call proxy
lr_proxy->if_proxy_client~execute(
EXPORTING
method_name = method
CHANGING
parmbind_tab = lt_parameter
).


* get payload
IF NOT lr_response_part IS INITIAL.
* try payload protocol
TRY.
IF NOT lr_prot_payload IS INITIAL.
lr_ws_payload =
lr_prot_payload->get_sent_response_payload( ).
IF NOT lr_ws_payload IS INITIAL.
org_response_data = lr_ws_payload->get_xml_binary( ).
ENDIF.
ENDIF.

CATCH cx_ai_system_fault. "#EC NO_HANDLER
ENDTRY.


ENDIF.
CATCH cx_ai_application_fault INTO lr_app_fault.


IF lr_prot_payload IS BOUND.
* read response payload
TRY.
lr_ws_payload =
lr_prot_payload->get_sent_exception_payload( ).
IF NOT lr_ws_payload IS INITIAL.
org_response_data = lr_ws_payload->get_xml_binary( ).
ENDIF.
CATCH cx_ai_system_fault. "#EC NO_HANDLER
ENDTRY.

ENDIF.


* get type description of application fault
lr_classdescr ?= cl_abap_typedescr=>describe_by_object_ref(
p_object_ref = lr_app_fault ).

* get type name
exception_class_name = lr_classdescr->get_relative_name( ).

* base class cannot be mapped to XML for no stylesheet can be
* generated
IF exception_class_name = 'CX_AI_APPLICATION_FAULT'.
RAISE EXCEPTION TYPE cx_ai_system_fault
EXPORTING
textid = cx_root=>cx_root
previous = lr_app_fault.
ENDIF.


CLEANUP.
IF lr_prot_payload IS BOUND.

* try to get original payload, even in case of errors
TRY.
lr_ws_payload = lr_prot_payload->get_sent_response_payload(
).
IF NOT lr_ws_payload IS INITIAL.
org_response_data = lr_ws_payload->get_xml_binary( ).
ENDIF.
CATCH cx_ai_system_fault. "#EC NO_HANDLER)
ENDTRY.

ENDIF.
ENDTRY.
resxml = org_response_data.

endmethod.

Sample program that uses the ZWS_CALL

constants:
lp TYPE prx_logical_port_name value 'Logical Port Name',
cl TYPE seoclname value 'Generated Consumer Proxy Class Name',
md_cdata TYPE seoclname value Generated method in the consumer proxy class'.

* Step 1

CALL TRANSFORMATION source_transformation
SOURCE root = inpdate
RESULT XML inpxml.

*Step 2
TRY.
CALL METHOD zcl_ws_call=>ws_call
EXPORTING
class = cl
method = md_sdata
logical_port = lp
inpxml = inpxml
IMPORTING
resxml = resxml.
CATCH cx_ai_system_fault .
ENDTRY.

* Step 3
CALL TRANSFORMATION Response_transformation
SOURCE XML resxml
RESULT root = sign_itab.

Conclusion

Even in a less than perfect world, SAP can easily consume web services. ABAP Consumer proxy generated class can be used to accomplish parts of the job that it is intended to do. By combining simple XML processing ABAP commands with the generated consumer proxy, every web service can be consumed in ABAP.

References:

SAP Developer Network SAP Weblogs: SAP Process Integration (PI)